From 6ad41e3ccd398a2721f41ad61c80b7bb03f7d127 Mon Sep 17 00:00:00 2001 From: Ittay Stern Date: Mon, 31 Dec 2018 17:21:27 +0200 Subject: Merge from ECOMP's repository Main Features -------------- - Async-Instantiation jobs mechanism major update; still WIP (package `org.onap.vid.job`) - New features in View/Edit: Activate fabric configuration; show related networks; soft delete - Support AAI service-tree traversal (`AAIServiceTree`) - In-memory cache for SDC models and certain A&AI queries (`CacheProviderWithLoadingCache`) - Upgrade TOSCA Parser and add parsing options; fix malformed TOSCA models - Resolve Cloud-Owner values for MSO - Pass X-ONAP headers to MSO Infrastructure -------------- - Remove codehaus' jackson mapper; use soley fasterxml 2.9.7 - Surefire invokes both TestNG and JUnit tests - Support Kotlin source files - AaiController2 which handles errors in a "Spring manner" - Inline generated-sources and remove jsonschema2pojo Quality -------- - Cumulative bug fixes (A&AI API, UI timeouts, and many more) - Many Sonar issues cleaned-up - Some unused classes removed - Minor changes in vid-automation project, allowing some API verification to run Hard Merges ------------ - HTTP Clients (MSO, A&AI, WebConfig, OutgoingRequestHeadersTest) - Moved `package org.onap.vid.controllers` to `controller`, without plural -- just to keep semantic sync with ECOMP. Reference commit in ECOMP: 3d1141625 Issue-ID: VID-378 Change-Id: I9c8d1e74caa41815891d441fc0760bb5f29c5788 Signed-off-by: Ittay Stern --- .../onap/vid/job/command/ALaCarteServiceCommand.kt | 122 ++++++++ .../ALaCarteServiceInstantiationCommand.java | 21 ++ .../job/command/BaseInProgressStatusCommand.java | 95 ++++++ .../vid/job/command/BaseInstantiationCommand.java | 18 ++ .../org/onap/vid/job/command/BaseRootCommand.java | 41 +++ .../onap/vid/job/command/BaseWatchingCommand.java | 70 +++++ .../java/org/onap/vid/job/command/CommandBase.java | 27 ++ .../onap/vid/job/command/CommandParentData.java | 80 +++++ .../org/onap/vid/job/command/CommandUtils.java | 46 +++ .../org/onap/vid/job/command/ExpiryChecker.java | 8 + .../org/onap/vid/job/command/HttpCallCommand.java | 8 +- .../vid/job/command/InProgressStatusCommand.java | 137 --------- .../vid/job/command/InProgressStatusService.java | 87 ++++++ .../onap/vid/job/command/InstanceGroupCommand.kt | 59 ++++ .../command/InstanceGroupInstantiationCommand.java | 47 +++ .../onap/vid/job/command/JobCommandFactory.java | 2 +- .../command/MacroServiceInstantiationCommand.java | 26 ++ .../java/org/onap/vid/job/command/MsoResult.kt | 9 + .../vid/job/command/MsoResultHandlerService.kt | 66 +++++ .../job/command/NetworkInstantiationCommand.java | 41 +++ .../org/onap/vid/job/command/ResourceCommand.kt | 324 +++++++++++++++++++++ .../command/ResourceInProgressStatusCommand.java | 32 ++ .../job/command/ResourceInstantiationCommand.java | 87 ++++++ .../ResourceWithChildrenInProgressCommand.java | 65 +++++ .../command/ServiceInProgressStatusCommand.java | 100 +++++++ .../job/command/ServiceInstantiationCommand.java | 146 +++------- .../job/command/VfmoduleInstantiationCommand.java | 44 +++ .../job/command/VnfInProgressStatusCommand.java | 87 ++++++ .../vid/job/command/VnfInstantiationCommand.java | 50 ++++ .../VolumeGroupInProgressStatusCommand.java | 65 +++++ .../command/VolumeGroupInstantiationCommand.java | 83 ++++++ .../onap/vid/job/command/WatchChildrenJobsBL.kt | 61 ++++ .../org/onap/vid/job/command/WatchingCommand.java | 33 +++ .../vid/job/command/WatchingCommandBaseModule.java | 93 ++++++ 34 files changed, 2033 insertions(+), 247 deletions(-) create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/ALaCarteServiceCommand.kt create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/ALaCarteServiceInstantiationCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/BaseInProgressStatusCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/BaseInstantiationCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/BaseRootCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/BaseWatchingCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/CommandBase.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/CommandParentData.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/CommandUtils.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/ExpiryChecker.java delete mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/InProgressStatusCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/InProgressStatusService.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupCommand.kt create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupInstantiationCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/MacroServiceInstantiationCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/MsoResult.kt create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/MsoResultHandlerService.kt create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/NetworkInstantiationCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/ResourceInProgressStatusCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/ResourceInstantiationCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/ResourceWithChildrenInProgressCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/ServiceInProgressStatusCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/VfmoduleInstantiationCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/VnfInProgressStatusCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/VnfInstantiationCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupInProgressStatusCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupInstantiationCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/WatchChildrenJobsBL.kt create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/WatchingCommand.java create mode 100644 vid-app-common/src/main/java/org/onap/vid/job/command/WatchingCommandBaseModule.java (limited to 'vid-app-common/src/main/java/org/onap/vid/job/command') diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ALaCarteServiceCommand.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/ALaCarteServiceCommand.kt new file mode 100644 index 000000000..f88e9d65e --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ALaCarteServiceCommand.kt @@ -0,0 +1,122 @@ +package org.onap.vid.job.command + +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate +import org.onap.vid.changeManagement.RequestDetailsWrapper +import org.onap.vid.job.* +import org.onap.vid.model.Action +import org.onap.vid.model.serviceInstantiation.ServiceInstantiation +import org.onap.vid.mso.RestMsoImplementation +import org.onap.vid.mso.model.ServiceDeletionRequestDetails +import org.onap.vid.properties.VidProperties +import org.onap.vid.services.AsyncInstantiationBusinessLogic +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.config.ConfigurableBeanFactory +import org.springframework.context.annotation.Scope +import org.springframework.http.HttpMethod +import org.springframework.stereotype.Component +import java.time.ZonedDateTime +import java.time.temporal.ChronoUnit +import java.util.* + +class ServiceExpiryChecker : ExpiryChecker { + + override fun isExpired(jobStartTime: ZonedDateTime?): Boolean { + val now = ZonedDateTime.now() + val maxHoursInProgress = VidProperties.getLongProperty(VidProperties.VID_JOB_MAX_HOURS_IN_PROGRESS) + val hoursBetween = ChronoUnit.HOURS.between(jobStartTime, now) + return maxHoursInProgress in 1..hoursBetween + } +} + + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +class ALaCarteServiceCommand @Autowired constructor( + inProgressStatusService: InProgressStatusService, + watchChildrenJobsBL: WatchChildrenJobsBL, + private val asyncInstantiationBL: AsyncInstantiationBusinessLogic, + private val jobsBrokerService: JobsBrokerService, + msoResultHandlerService: MsoResultHandlerService, + private val jobAdapter: JobAdapter, + restMso: RestMsoImplementation +) : ResourceCommand(restMso, inProgressStatusService, msoResultHandlerService, watchChildrenJobsBL), JobCommand { + + override fun getExpiryChecker(): ExpiryChecker { + return ServiceExpiryChecker(); + } + + companion object { + private val LOGGER = EELFLoggerDelegate.getLogger(ALaCarteServiceCommand::class.java) + } + + override fun getRequest(): ServiceInstantiation { + return msoResultHandlerService.getRequest(sharedData) + } + + override fun createChildren(): Job.JobStatus { + val dataForChild = buildDataForChild(getRequest())//.plus(ACTION_PHASE to actionPhase) + + val childJobType = when (actionPhase) { + Action.Create -> JobType.InstanceGroupInstantiation + Action.Delete -> JobType.InstanceGroup + else -> return Job.JobStatus.COMPLETED + } + + childJobs = getRequest().vnfGroups + .map { jobAdapter.createChildJob(childJobType, Job.JobStatus.CREATING, it.value, sharedData, dataForChild) } + .map { jobsBrokerService.add(it) } + .map { it.toString() } + + return Job.JobStatus.COMPLETED_WITH_NO_ACTION + } + + private fun buildDataForChild(request: ServiceInstantiation): Map { + val commandParentData = CommandParentData() + commandParentData.addInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID, request.instanceId) + commandParentData.addModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO, request.modelInfo) + return commandParentData.parentData + } + + override fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan { + TODO("not implemented") + } + + override fun planDeleteMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan { + val requestDetailsWrapper = generateServiceDeletionRequest() + val path = asyncInstantiationBL.getServiceDeletionPath(getRequest().instanceId) + return MsoRestCallPlan(HttpMethod.DELETE, path, Optional.of(requestDetailsWrapper), Optional.empty(), + "delete instance with id ${getRequest().instanceId}") + + } + + override fun handleInProgressStatus(jobStatus: Job.JobStatus): Job.JobStatus { + if (jobStatus==Job.JobStatus.FAILED) { + asyncInstantiationBL.handleFailedInstantiation(sharedData.jobUuid) + return jobStatus + } + + asyncInstantiationBL.updateServiceInfoAndAuditStatus(sharedData.jobUuid, jobStatus) + return if (jobStatus == Job.JobStatus.PAUSE) Job.JobStatus.IN_PROGRESS else jobStatus + } + + + private fun generateServiceDeletionRequest(): RequestDetailsWrapper { + return asyncInstantiationBL.generateALaCarteServiceDeletionRequest( + sharedData.jobUuid, getRequest(), sharedData.userId + ) + } + + override fun getExternalInProgressStatus() = Job.JobStatus.IN_PROGRESS + + override fun isServiceCommand(): Boolean = true + + override fun onFinal(jobStatus: Job.JobStatus) { + asyncInstantiationBL.updateServiceInfoAndAuditStatus(sharedData.jobUuid, jobStatus) + } + + override fun onInitial(phase: Action) { + if (phase== Action.Delete) { + asyncInstantiationBL.updateServiceInfoAndAuditStatus(sharedData.jobUuid, Job.JobStatus.IN_PROGRESS) + } + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ALaCarteServiceInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/ALaCarteServiceInstantiationCommand.java new file mode 100644 index 000000000..8a1346d2e --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ALaCarteServiceInstantiationCommand.java @@ -0,0 +1,21 @@ +package org.onap.vid.job.command; + +import org.onap.vid.changeManagement.RequestDetailsWrapper; +import org.onap.vid.job.JobCommand; +import org.onap.vid.mso.model.ServiceInstantiationRequestDetails; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class ALaCarteServiceInstantiationCommand extends ServiceInstantiationCommand implements JobCommand { + + @Override + protected RequestDetailsWrapper generateServiceInstantiationRequest() { + return asyncInstantiationBL.generateALaCarteServiceInstantiationRequest( + getSharedData().getJobUuid(), getRequest(), optimisticUniqueServiceInstanceName, getSharedData().getUserId() + ); + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/BaseInProgressStatusCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/BaseInProgressStatusCommand.java new file mode 100644 index 000000000..a035708f2 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/BaseInProgressStatusCommand.java @@ -0,0 +1,95 @@ +package org.onap.vid.job.command; + +import com.google.common.collect.ImmutableMap; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.vid.job.*; +import org.onap.vid.job.impl.JobSharedData; +import org.onap.vid.mso.RestMsoImplementation; +import org.onap.vid.mso.RestObject; +import org.onap.vid.mso.rest.AsyncRequestStatus; +import org.onap.vid.services.AsyncInstantiationBusinessLogic; +import org.togglz.core.manager.FeatureManager; + +import javax.inject.Inject; +import java.util.Map; + +public abstract class BaseInProgressStatusCommand extends BaseInstantiationCommand implements JobCommand { + private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(BaseInProgressStatusCommand.class); + + @Inject + protected AsyncInstantiationBusinessLogic asyncInstantiationBL; + + @Inject + protected JobsBrokerService jobsBrokerService; + + @Inject + protected JobAdapter jobAdapter; + + @Inject + protected RestMsoImplementation restMso; + + @Inject + protected FeatureManager featureManager; + + @Inject + protected InProgressStatusService inProgressStatusService; + + + protected String requestId; + + protected String instanceId; + + + @Override + public NextCommand call() { + + try { + Job.JobStatus jobStatus = inProgressStatusService.call(getExpiryChecker(), getSharedData(), requestId); + return processJobStatus(jobStatus); + } catch (javax.ws.rs.ProcessingException e) { + // Retry when we can't connect MSO during getStatus + LOGGER.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, will retry: {}", requestId, e, e); + return new NextCommand(Job.JobStatus.IN_PROGRESS, this); + } catch (InProgressStatusService.BadResponseFromMso e) { + return handleFailedMsoResponse(e.getMsoResponse()); + } + catch (RuntimeException e) { + LOGGER.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, stopping: {}", requestId, e, e); + return new NextCommand(Job.JobStatus.STOPPED, this); + } + } + + protected abstract ExpiryChecker getExpiryChecker(); + + abstract NextCommand processJobStatus(Job.JobStatus jobStatus); + + private NextCommand handleFailedMsoResponse(RestObject msoResponse) { + inProgressStatusService.handleFailedMsoResponse(getSharedData().getJobUuid(), requestId, msoResponse); + return new NextCommand(Job.JobStatus.IN_PROGRESS, this); + } + + @Override + public BaseInProgressStatusCommand init(JobSharedData sharedData, Map commandData) { + return init(sharedData, (String) commandData.get("requestId"), (String) commandData.get("instanceId")); + } + + + protected BaseInProgressStatusCommand init(JobSharedData sharedData, + String requestId, + String instanceId) { + init(sharedData); + this.requestId = requestId; + this.instanceId = instanceId; + return this; + } + + @Override + public Map getData() { + return ImmutableMap.of( + "requestId", requestId, + "instanceId", instanceId + ); + } + + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/BaseInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/BaseInstantiationCommand.java new file mode 100644 index 000000000..6f78eafab --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/BaseInstantiationCommand.java @@ -0,0 +1,18 @@ +package org.onap.vid.job.command; + +import org.onap.vid.job.impl.JobSharedData; + +import java.util.Map; + + +public abstract class BaseInstantiationCommand extends CommandBase{ + + + protected CommandParentData commandParentData = new CommandParentData(); + + protected BaseInstantiationCommand init(JobSharedData sharedData, Map commandData) { + super.init(sharedData); + commandParentData.initParentData(commandData); + return this; + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/BaseRootCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/BaseRootCommand.java new file mode 100644 index 000000000..4930c94c3 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/BaseRootCommand.java @@ -0,0 +1,41 @@ +package org.onap.vid.job.command; + +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.impl.JobSharedData; +import org.onap.vid.model.RequestReferencesContainer; +import org.onap.vid.model.serviceInstantiation.ServiceInstantiation; +import org.onap.vid.mso.RestObject; + +import javax.inject.Inject; + + +public abstract class BaseRootCommand extends CommandBase{ + + @Inject + private MsoResultHandlerService msoResultHandlerService; + + @Override + protected CommandBase init(JobSharedData sharedData) { + super.init(sharedData); + return this; + } + + protected ServiceInstantiation getRequest() { + return msoResultHandlerService.getRequest(getSharedData()); + } + + protected NextCommand handleRootResponse(RestObject msoResponse){ + MsoResult msoResult = msoResultHandlerService.handleRootResponse(getSharedData().getJobUuid(), msoResponse); + return new NextCommand(msoResult.getJobStatus(), + (msoResult.getMsoResourceIds()!=null) ? + new ServiceInProgressStatusCommand(getSharedData(), msoResult.getMsoResourceIds()) : + null + ); + + } + + protected NextCommand handleCommandFailed() { + return new NextCommand(msoResultHandlerService.handleRootCommandFailed(getSharedData().getJobUuid()).getJobStatus()); + } + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/BaseWatchingCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/BaseWatchingCommand.java new file mode 100644 index 000000000..b619eddfd --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/BaseWatchingCommand.java @@ -0,0 +1,70 @@ +package org.onap.vid.job.command; + +import org.apache.commons.lang3.ObjectUtils; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.vid.job.Job; +import org.onap.vid.job.JobCommand; +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.impl.JobSharedData; +import org.onap.vid.services.AsyncInstantiationBusinessLogic; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class BaseWatchingCommand extends BaseInstantiationCommand implements JobCommand { + + private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(BaseWatchingCommand.class); + + @Inject + protected AsyncInstantiationBusinessLogic asyncInstantiationBL; + + @Inject + private WatchChildrenJobsBL watchChildrenJobsBL; + + private List childrenJobsIds; + + protected boolean isService; + + public BaseWatchingCommand() {} + + public BaseWatchingCommand(JobSharedData sharedData, List childrenJobsIds, boolean isService) { + init(sharedData, childrenJobsIds, isService); + } + + @Override + public NextCommand call() { + Job.JobStatus cumulativeJobsStatus = watchChildrenJobsBL.cumulateJobStatus( + watchChildrenJobsBL.retrieveChildrenJobsStatus(childrenJobsIds), + Job.JobStatus.COMPLETED); + return getNextCommand(cumulativeJobsStatus); + } + + protected abstract NextCommand getNextCommand(Job.JobStatus cumulativeJobsStatus); + + @Override + public BaseWatchingCommand init(JobSharedData sharedData, Map commandData) { + return init( + sharedData, + (List) commandData.get("childrenJobs"), + (boolean) commandData.get("isService") + ); + } + + protected BaseWatchingCommand init(JobSharedData sharedData, List childrenJobsIds, boolean isService) { + super.init(sharedData); + this.childrenJobsIds = ObjectUtils.defaultIfNull(childrenJobsIds, new ArrayList<>()); + this.isService = isService; + return this; + } + + @Override + public Map getData() { + Map data = new HashMap<>(); + data.put("childrenJobs", childrenJobsIds); + data.put("isService", isService); + return data; + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/CommandBase.java b/vid-app-common/src/main/java/org/onap/vid/job/command/CommandBase.java new file mode 100644 index 000000000..163106c09 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/CommandBase.java @@ -0,0 +1,27 @@ +package org.onap.vid.job.command; + +import org.onap.vid.job.impl.JobSharedData; + +import javax.inject.Inject; + + +public abstract class CommandBase { + + @Inject + protected CommandUtils commandUtils; + + private JobSharedData sharedData; + + protected CommandBase init(JobSharedData sharedData) { + this.setSharedData(sharedData); + return this; + } + + public JobSharedData getSharedData() { + return sharedData; + } + + private void setSharedData(JobSharedData sharedData) { + this.sharedData = sharedData; + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/CommandParentData.java b/vid-app-common/src/main/java/org/onap/vid/job/command/CommandParentData.java new file mode 100644 index 000000000..25c197278 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/CommandParentData.java @@ -0,0 +1,80 @@ +package org.onap.vid.job.command; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.onap.vid.mso.model.ModelInfo; + +import java.util.HashMap; +import java.util.Map; + +public class CommandParentData { + + + public enum CommandDataKey{ + SERVICE_MODEL_INFO, + SERVICE_INSTANCE_ID, + VNF_INSTANCE_ID, + VNF_MODEL_INFO, + VG_INSTANCE_ID, + ; + } + + private static final String RESOURCE_INSTANCE_IDS = "resourceInstancesIds"; + private static final String RESOURCE_MODEL_INFOS = "resourceModelInfos"; + + private final TypeReference> mapCommandKeyToString = + new TypeReference> () {}; + + private final TypeReference> mapCommandKeyToModelInfo = + new TypeReference> () {}; + + + private ObjectMapper objectMapper = new ObjectMapper(); + + private Map getModelInfosByCommandData(Map commandData) { + Object object = commandData.get(RESOURCE_MODEL_INFOS); + if (object != null) { + return objectMapper.convertValue(object, mapCommandKeyToModelInfo); + } + return null; + } + + private Map getInstanceIdsByCommandData(Map commandData) { + Object object = commandData.get(RESOURCE_INSTANCE_IDS); + if (object != null) { + return objectMapper.convertValue(object, mapCommandKeyToString); + } + return null; + } + + public Map getParentData() { + Map data = new HashMap<>(); + data.put(RESOURCE_INSTANCE_IDS, resourceInstancesIds); + data.put(RESOURCE_MODEL_INFOS, resourceModelInfos); + return data; + } + private Map resourceInstancesIds = new HashMap<>(); + private Map resourceModelInfos = new HashMap<>(); + + public void addModelInfo(CommandDataKey modelInfoKey, ModelInfo modelInfo) { + resourceModelInfos.put(modelInfoKey, modelInfo); + } + + public void addInstanceId(CommandDataKey instanceIdKey, String instanceId) { + resourceInstancesIds.put(instanceIdKey, instanceId); + } + public ModelInfo getModelInfo(CommandDataKey modelInfoKey) { + return resourceModelInfos.get(modelInfoKey); + } + + public String getInstanceId(CommandDataKey instanceIdKey) { + return resourceInstancesIds.get(instanceIdKey); + } + + public CommandParentData initParentData(Map commandData) { + resourceModelInfos = getModelInfosByCommandData(commandData); + resourceInstancesIds = getInstanceIdsByCommandData(commandData); + return this; + } + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/CommandUtils.java b/vid-app-common/src/main/java/org/onap/vid/job/command/CommandUtils.java new file mode 100644 index 000000000..497eafda9 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/CommandUtils.java @@ -0,0 +1,46 @@ +package org.onap.vid.job.command; + +import org.apache.commons.lang3.StringUtils; +import org.onap.vid.asdc.AsdcCatalogException; +import org.onap.vid.model.ServiceModel; +import org.onap.vid.services.VidService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class CommandUtils { + + private final VidService vidService; + + @Autowired + public CommandUtils(VidService vidService) { + this.vidService = vidService; + } + + public boolean isVfModuleBaseModule(String serviceModelUuid, String vfModuleModelUUID) throws AsdcCatalogException{ + ServiceModel serviceModel = vidService.getService(serviceModelUuid); + + if (serviceModel==null) { + throw new AsdcCatalogException("Failed to retrieve model with uuid "+serviceModelUuid +" from SDC"); + } + + if (serviceModel.getVfModules() == null) { + throw createAsdcCatalogVfModuleModelUUIDNotFoundException(serviceModelUuid, vfModuleModelUUID); + } + + return serviceModel.getVfModules() + .values() + .stream() + .filter(vfModule -> StringUtils.equals(vfModule.getUuid(), vfModuleModelUUID)) + .findFirst() + .orElseThrow(() -> createAsdcCatalogVfModuleModelUUIDNotFoundException(serviceModelUuid, vfModuleModelUUID)) + .getProperties() + .getBaseModule(); + } + + private AsdcCatalogException createAsdcCatalogVfModuleModelUUIDNotFoundException(String serviceModelUuid, String vfModuleModelUUID) { + return new AsdcCatalogException("Failed to find vfModuleModelUUID: " + vfModuleModelUUID + + "in model with uuid: " + serviceModelUuid); + } + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ExpiryChecker.java b/vid-app-common/src/main/java/org/onap/vid/job/command/ExpiryChecker.java new file mode 100644 index 000000000..1ae5d2185 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ExpiryChecker.java @@ -0,0 +1,8 @@ +package org.onap.vid.job.command; + +import java.time.ZonedDateTime; + +public interface ExpiryChecker { + + boolean isExpired(ZonedDateTime jobStartTime); +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/HttpCallCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/HttpCallCommand.java index 5951d7c83..7ee48a8e1 100644 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/HttpCallCommand.java +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/HttpCallCommand.java @@ -4,13 +4,13 @@ import com.google.common.collect.ImmutableMap; import org.onap.vid.job.Job; import org.onap.vid.job.JobCommand; import org.onap.vid.job.NextCommand; +import org.onap.vid.job.impl.JobSharedData; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; -import javax.ws.rs.core.Response; import java.util.Map; import java.util.UUID; @@ -30,13 +30,13 @@ public class HttpCallCommand implements JobCommand { @Override public NextCommand call() { - final Response response = ClientBuilder.newClient().target(url).request().post(Entity.text(uuid.toString())); + ClientBuilder.newClient().target(url).request().post(Entity.text(uuid.toString())); return new NextCommand(Job.JobStatus.COMPLETED); } @Override - public HttpCallCommand init(UUID jobUuid, Map data) { - return init((String) data.get("url"), jobUuid); + public HttpCallCommand init(JobSharedData sharedData, Map commandData) { + return init((String) commandData.get("url"), sharedData.getJobUuid()); } private HttpCallCommand init(String url, UUID jobUuid) { diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/InProgressStatusCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/InProgressStatusCommand.java deleted file mode 100644 index 6685a63d6..000000000 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/InProgressStatusCommand.java +++ /dev/null @@ -1,137 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2018 Nokia. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.vid.job.command; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.ImmutableMap; -import io.joshworks.restclient.http.HttpResponse; -import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; -import org.onap.vid.job.Job.JobStatus; -import org.onap.vid.job.JobCommand; -import org.onap.vid.job.NextCommand; -import org.onap.vid.mso.MsoInterface; -import org.onap.vid.mso.rest.AsyncRequestStatus; -import org.onap.vid.services.AsyncInstantiationBusinessLogic; -import org.onap.vid.services.AuditService; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; - -import javax.inject.Inject; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; - - -@Component -@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) -public class InProgressStatusCommand implements JobCommand { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(InProgressStatusCommand.class); - - @Inject - private AsyncInstantiationBusinessLogic asyncInstantiationBL; - - @Inject - private MsoInterface restMso; - - @Inject - private AuditService auditService; - - private String requestId; - - private UUID jobUuid; - - public InProgressStatusCommand() { - } - - InProgressStatusCommand(UUID jobUuid, String requestId) { - init(jobUuid, requestId); - } - - InProgressStatusCommand(AsyncInstantiationBusinessLogic asyncInstantiationBusinessLogic, MsoInterface msoInterface, AuditService auditService, UUID jobUuid, String requestId) { - this(jobUuid, requestId); - this.asyncInstantiationBL = asyncInstantiationBusinessLogic; - this.restMso = msoInterface; - this.auditService = auditService; - } - - @Override - public NextCommand call() { - - try { - String path = asyncInstantiationBL.getOrchestrationRequestsPath() + "/" + requestId; - HttpResponse msoResponse = restMso.get(path, AsyncRequestStatus.class); - - - JobStatus jobStatus; - if (msoResponse.getStatus() >= 400 || msoResponse.getBody() == null) { - auditService.setFailedAuditStatusFromMso(jobUuid, requestId, msoResponse.getStatus(), Objects.toString(msoResponse.getBody())); - LOGGER.error(EELFLoggerDelegate.errorLogger, - "Failed to get orchestration status for {}. Status code: {}, Body: {}", - requestId, msoResponse.getStatus(), Objects.toString(msoResponse.getRawBody())); - return new NextCommand(JobStatus.IN_PROGRESS, this); - } else { - jobStatus = asyncInstantiationBL.calcStatus(msoResponse.getBody()); - } - - asyncInstantiationBL.auditMsoStatus(jobUuid, msoResponse.getBody().request); - - - if (jobStatus == JobStatus.FAILED) { - asyncInstantiationBL.handleFailedInstantiation(jobUuid); - } else { - asyncInstantiationBL.updateServiceInfoAndAuditStatus(jobUuid, jobStatus); - } - //in case of JobStatus.PAUSE we leave the job itself as IN_PROGRESS, for keep tracking job progress - if (jobStatus == JobStatus.PAUSE) { - return new NextCommand(JobStatus.IN_PROGRESS, this); - } - return new NextCommand(jobStatus, this); - } catch (javax.ws.rs.ProcessingException e) { - // Retry when we can't connect MSO during getStatus - LOGGER.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, will retry: {}", requestId, e, e); - return new NextCommand(JobStatus.IN_PROGRESS, this); - } catch (RuntimeException e) { - LOGGER.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, stopping: {}", requestId, e, e); - return new NextCommand(JobStatus.STOPPED, this); - } - } - - @Override - public InProgressStatusCommand init(UUID jobUuid, Map data) { - return init(jobUuid, (String) data.get("requestId")); - } - - private InProgressStatusCommand init(UUID jobUuid, String requestId) { - this.requestId = requestId; - this.jobUuid = jobUuid; - return this; - } - - @Override - public Map getData() { - return ImmutableMap.of("requestId", requestId); - } - -} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/InProgressStatusService.java b/vid-app-common/src/main/java/org/onap/vid/job/command/InProgressStatusService.java new file mode 100644 index 000000000..4799f0087 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/InProgressStatusService.java @@ -0,0 +1,87 @@ +package org.onap.vid.job.command; + +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.vid.job.Job; +import org.onap.vid.job.impl.JobSharedData; +import org.onap.vid.mso.RestMsoImplementation; +import org.onap.vid.mso.RestObject; +import org.onap.vid.mso.rest.AsyncRequestStatus; +import org.onap.vid.services.AsyncInstantiationBusinessLogic; +import org.onap.vid.services.AuditService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; +import java.util.UUID; + +import static org.onap.vid.utils.TimeUtils.parseZonedDateTime; + +@Service +public class InProgressStatusService { + + private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(InProgressStatusService.class); + + private final AsyncInstantiationBusinessLogic asyncInstantiationBL; + + private final RestMsoImplementation restMso; + + private final AuditService auditService; + + @Autowired + public InProgressStatusService(AsyncInstantiationBusinessLogic asyncInstantiationBL, RestMsoImplementation restMso, AuditService auditService) { + this.asyncInstantiationBL = asyncInstantiationBL; + this.restMso = restMso; + this.auditService = auditService; + } + + + public Job.JobStatus call(ExpiryChecker expiryChecker, JobSharedData sharedData, String requestId) { + + RestObject asyncRequestStatus = getAsyncRequestStatus(requestId); + asyncInstantiationBL.auditMsoStatus(sharedData.getRootJobId(), asyncRequestStatus.get().request); + Job.JobStatus jobStatus = asyncInstantiationBL.calcStatus(asyncRequestStatus.get()); + ZonedDateTime jobStartTime = getZonedDateTime(asyncRequestStatus, requestId); + jobStatus = expiryChecker.isExpired(jobStartTime) ? Job.JobStatus.FAILED : jobStatus; + return jobStatus; + } + + private RestObject getAsyncRequestStatus(String requestId) { + String path = asyncInstantiationBL.getOrchestrationRequestsPath()+"/"+requestId; + RestObject msoResponse = restMso.GetForObject(path, AsyncRequestStatus.class); + if (msoResponse.getStatusCode() >= 400 || msoResponse.get() == null) { + throw new BadResponseFromMso(msoResponse); + } + return msoResponse; + } + + public void handleFailedMsoResponse(UUID jobUUID, String requestId, RestObject msoResponse) { + auditService.setFailedAuditStatusFromMso(jobUUID, requestId, msoResponse.getStatusCode(), msoResponse.getRaw()); + LOGGER.error(EELFLoggerDelegate.errorLogger, + "Failed to get orchestration status for {}. Status code: {}, Body: {}", + requestId, msoResponse.getStatusCode(), msoResponse.getRaw()); + } + + public static class BadResponseFromMso extends RuntimeException { + private final RestObject msoResponse; + + public BadResponseFromMso(RestObject msoResponse) { + this.msoResponse = msoResponse; + } + + public RestObject getMsoResponse() { + return msoResponse; + } + } + + private ZonedDateTime getZonedDateTime(RestObject asyncRequestStatusResponse, String requestId) { + ZonedDateTime jobStartTime; + try { + jobStartTime = parseZonedDateTime(asyncRequestStatusResponse.get().request.startTime); + } catch (DateTimeParseException | NullPointerException e) { + LOGGER.error(EELFLoggerDelegate.errorLogger, "Failed to parse start time for {}, body: {}. Current time will be used", requestId, asyncRequestStatusResponse.getRaw(), e); + jobStartTime = ZonedDateTime.now(); + } + return jobStartTime; + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupCommand.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupCommand.kt new file mode 100644 index 000000000..36da5084b --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupCommand.kt @@ -0,0 +1,59 @@ +package org.onap.vid.job.command + +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate +import org.onap.vid.job.Job +import org.onap.vid.job.JobAdapter +import org.onap.vid.job.JobCommand +import org.onap.vid.model.serviceInstantiation.InstanceGroup +import org.onap.vid.mso.RestMsoImplementation +import org.onap.vid.services.AsyncInstantiationBusinessLogic +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.config.ConfigurableBeanFactory +import org.springframework.context.annotation.Scope +import org.springframework.http.HttpMethod +import org.springframework.stereotype.Component +import java.util.* + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +class InstanceGroupCommand @Autowired constructor( + private val asyncInstantiationBL: AsyncInstantiationBusinessLogic, + restMso: RestMsoImplementation, + msoResultHandlerService: MsoResultHandlerService, + inProgressStatusService:InProgressStatusService, + watchChildrenJobsBL: WatchChildrenJobsBL +) : ResourceCommand(restMso, inProgressStatusService, msoResultHandlerService, watchChildrenJobsBL), JobCommand { + + companion object { + private val LOGGER = EELFLoggerDelegate.getLogger(InstanceGroupCommand::class.java) + } + + override fun createChildren(): Job.JobStatus { + return Job.JobStatus.COMPLETED_WITH_NO_ACTION + } + + override fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan { + val serviceInstanceId = commandParentData.getInstanceId(CommandParentData.CommandDataKey.SERVICE_INSTANCE_ID) + val serviceModelInfo = commandParentData.getModelInfo(CommandParentData.CommandDataKey.SERVICE_MODEL_INFO) + + val instantiatePath = asyncInstantiationBL.getInstanceGroupInstantiationPath() + + val requestDetailsWrapper = asyncInstantiationBL.generateInstanceGroupInstantiationRequest( + request as InstanceGroup, + serviceModelInfo, serviceInstanceId, + userId + ) + + val actionDescription = "create instance group in $serviceInstanceId" + + return MsoRestCallPlan(HttpMethod.POST, instantiatePath, Optional.of(requestDetailsWrapper), Optional.empty(), actionDescription) + } + + override fun planDeleteMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan { + val path = asyncInstantiationBL.getInstanceGroupDeletePath(getRequest().instanceId) + return MsoRestCallPlan(HttpMethod.DELETE, path, Optional.empty(), Optional.of(userId), + "delete instance group with id ${getRequest().instanceId}") + + } + +} \ No newline at end of file diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupInstantiationCommand.java new file mode 100644 index 000000000..17b3f3552 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/InstanceGroupInstantiationCommand.java @@ -0,0 +1,47 @@ +package org.onap.vid.job.command;//package org.onap.vid.job.command; + +import org.onap.vid.changeManagement.RequestDetailsWrapper; +import org.onap.vid.job.JobAdapter; +import org.onap.vid.job.command.CommandParentData.CommandDataKey; +import org.onap.vid.model.Action; +import org.onap.vid.model.serviceInstantiation.InstanceGroup; +import org.onap.vid.mso.model.InstanceGroupInstantiationRequestDetails; +import org.onap.vid.services.AsyncInstantiationBusinessLogic; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class InstanceGroupInstantiationCommand extends ResourceInstantiationCommand { + + @Inject + private AsyncInstantiationBusinessLogic asyncInstantiationBL; + + @Override + protected String getRequestPath() { + return asyncInstantiationBL.getInstanceGroupInstantiationPath(); + } + + @Override + protected RequestDetailsWrapper generateMSORequest(JobAdapter.AsyncJobRequest request, String userId) { + return asyncInstantiationBL.generateInstanceGroupInstantiationRequest( + (InstanceGroup) getSharedData().getRequest(), + commandParentData.getModelInfo(CommandDataKey.SERVICE_MODEL_INFO), + commandParentData.getInstanceId(CommandDataKey.SERVICE_INSTANCE_ID), + getSharedData().getUserId() + ); + } + + @Override + protected String getJobAuditMSOStatus() { + return "INSTANCE_GROUP_REQUESTED"; + } + + @Override + protected boolean shouldInstantiateMyself() { + return Action.Create == ((InstanceGroup) getSharedData().getRequest()).getAction(); + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/JobCommandFactory.java b/vid-app-common/src/main/java/org/onap/vid/job/command/JobCommandFactory.java index 1e613c58b..5661663b2 100644 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/JobCommandFactory.java +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/JobCommandFactory.java @@ -34,7 +34,7 @@ public class JobCommandFactory { public JobCommand toCommand(Job job) { final JobCommand command = jobFactory.apply(job.getType().getCommandClass()); - command.init(job.getUuid(), job.getData()); + command.init(job.getSharedData(), job.getData()); return command; } diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/MacroServiceInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/MacroServiceInstantiationCommand.java new file mode 100644 index 000000000..e03f9b8d7 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/MacroServiceInstantiationCommand.java @@ -0,0 +1,26 @@ +package org.onap.vid.job.command; + +import org.onap.vid.changeManagement.RequestDetailsWrapper; +import org.onap.vid.job.JobCommand; +import org.onap.vid.mso.model.ServiceInstantiationRequestDetails; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class MacroServiceInstantiationCommand extends ServiceInstantiationCommand implements JobCommand { + + public MacroServiceInstantiationCommand() { + // empty constructor + } + + @Override + protected RequestDetailsWrapper generateServiceInstantiationRequest() { + return asyncInstantiationBL.generateMacroServiceInstantiationRequest( + getSharedData().getJobUuid(), getRequest(), optimisticUniqueServiceInstanceName, getSharedData().getUserId() + ); + } + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/MsoResult.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/MsoResult.kt new file mode 100644 index 000000000..95f520f80 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/MsoResult.kt @@ -0,0 +1,9 @@ +package org.onap.vid.job.command + +import org.onap.vid.job.Job + +data class MsoResourceIds (val requestId:String, val instanceId:String) + +val EMPTY_MSO_RESOURCE_ID = MsoResourceIds("","") + +data class MsoResult @JvmOverloads constructor(val jobStatus: Job.JobStatus, val msoResourceIds: MsoResourceIds = EMPTY_MSO_RESOURCE_ID) diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/MsoResultHandlerService.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/MsoResultHandlerService.kt new file mode 100644 index 000000000..9760f667f --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/MsoResultHandlerService.kt @@ -0,0 +1,66 @@ +package org.onap.vid.job.command + +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate +import org.onap.vid.job.Job +import org.onap.vid.job.impl.JobSharedData +import org.onap.vid.model.RequestReferencesContainer +import org.onap.vid.model.serviceInstantiation.ServiceInstantiation +import org.onap.vid.mso.RestObject +import org.onap.vid.services.AsyncInstantiationBusinessLogic +import org.onap.vid.services.AuditService +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service +import java.util.* + +@Service +class MsoResultHandlerService +@Autowired constructor(private val asyncInstantiationBL: AsyncInstantiationBusinessLogic, private val auditService: AuditService) { + + companion object { + private val LOGGER = EELFLoggerDelegate.getLogger(MsoResultHandlerService::class.java) + } + + fun getRequest(jobSharedData: JobSharedData): ServiceInstantiation { + return jobSharedData.request as ServiceInstantiation + } + + fun handleRootResponse(jobUUID: UUID, msoResponse: RestObject): MsoResult { + return if (msoResponse.statusCode in 200..399) { + val jobStatus = Job.JobStatus.IN_PROGRESS + val msoResourceIds = MsoResourceIds(msoResponse.get().requestReferences.requestId, msoResponse.get().requestReferences.instanceId) + asyncInstantiationBL.auditVidStatus(jobUUID, jobStatus) + setInitialRequestAuditStatusFromMso(jobUUID, msoResourceIds.requestId) + asyncInstantiationBL.updateServiceInfo(jobUUID) { x -> + x.jobStatus = jobStatus + x.serviceInstanceId = msoResourceIds.instanceId + x.msoRequestId = UUID.fromString(msoResourceIds.requestId) + } + MsoResult(jobStatus, msoResourceIds) + } else { + auditService.setFailedAuditStatusFromMso(jobUUID, null, msoResponse.statusCode, msoResponse.raw) + handleRootCommandFailed(jobUUID) + } + } + + fun handleResponse(msoResponse: RestObject, actionDescription: String): MsoResult { + return if (msoResponse.statusCode in 200..399) { + val msoResourceIds = MsoResourceIds(msoResponse.get().requestReferences.requestId, msoResponse.get().requestReferences.instanceId) + LOGGER.debug("Successfully sent $actionDescription. Request id: ${msoResourceIds.requestId}") + MsoResult(Job.JobStatus.COMPLETED_WITH_NO_ACTION, msoResourceIds) + } else { + LOGGER.debug("Failed to $actionDescription. Details: ${msoResponse.raw}") + MsoResult(Job.JobStatus.FAILED) + } + } + + + fun handleRootCommandFailed(jobUUID: UUID): MsoResult { + asyncInstantiationBL.handleFailedInstantiation(jobUUID) + return MsoResult(Job.JobStatus.FAILED) + } + + private fun setInitialRequestAuditStatusFromMso(jobUUID: UUID, requestId: String) { + val initialMsoRequestStatus = "REQUESTED" + asyncInstantiationBL.auditMsoStatus(jobUUID, initialMsoRequestStatus, requestId, null) + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/NetworkInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/NetworkInstantiationCommand.java new file mode 100644 index 000000000..401bab723 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/NetworkInstantiationCommand.java @@ -0,0 +1,41 @@ +package org.onap.vid.job.command; + +import org.onap.vid.changeManagement.RequestDetailsWrapper; +import org.onap.vid.job.JobAdapter; +import org.onap.vid.job.command.CommandParentData.CommandDataKey; +import org.onap.vid.model.serviceInstantiation.Network; +import org.onap.vid.mso.model.NetworkInstantiationRequestDetails; +import org.onap.vid.services.AsyncInstantiationBusinessLogic; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class NetworkInstantiationCommand extends ResourceInstantiationCommand { + + @Inject + private AsyncInstantiationBusinessLogic asyncInstantiationBL; + + @Override + protected String getRequestPath() { + return asyncInstantiationBL.getNetworkInstantiationPath(commandParentData.getInstanceId(CommandDataKey.SERVICE_INSTANCE_ID)); + } + + @Override + protected RequestDetailsWrapper generateMSORequest(JobAdapter.AsyncJobRequest request, String userId) { + return asyncInstantiationBL.generateNetworkInstantiationRequest( + (Network) getSharedData().getRequest(), + commandParentData.getModelInfo(CommandDataKey.SERVICE_MODEL_INFO), + commandParentData.getInstanceId(CommandDataKey.SERVICE_INSTANCE_ID), + getSharedData().getUserId() + ); + } + + @Override + protected String getJobAuditMSOStatus() { + return "NETWORK_REQUESTED"; + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt new file mode 100644 index 000000000..6dfcbdf3a --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt @@ -0,0 +1,324 @@ +package org.onap.vid.job.command + +import com.fasterxml.jackson.module.kotlin.convertValue +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate +import org.onap.vid.changeManagement.RequestDetailsWrapper +import org.onap.vid.job.Job +import org.onap.vid.job.Job.JobStatus +import org.onap.vid.job.JobAdapter +import org.onap.vid.job.JobCommand +import org.onap.vid.job.NextCommand +import org.onap.vid.job.impl.JobSharedData +import org.onap.vid.model.Action +import org.onap.vid.model.RequestReferencesContainer +import org.onap.vid.model.serviceInstantiation.BaseResource +import org.onap.vid.mso.RestMsoImplementation +import org.onap.vid.utils.JACKSON_OBJECT_MAPPER +import org.onap.vid.utils.getEnumFromMapOfStrings +import org.springframework.http.HttpMethod +import java.util.* + +const val INTERNAL_STATE = "internalState" +const val ACTION_PHASE = "actionPhase" +const val CHILD_JOBS = "childJobs" +const val MSO_RESOURCE_ID = "msoResourceIds" +const val CUMULATIVE_STATUS = "cumulativeStatus" + +enum class InternalState constructor(val immediate:Boolean=false) { + INITIAL, + CREATING_CHILDREN(true), + WATCHING, + DELETE_MYSELF, + CREATE_MYSELF, + IN_PROGRESS, + TERMINAL +} + +data class NextInternalState(val nextActionPhase: Action, val nextInternalState: InternalState) + + +data class MsoRestCallPlan( + val httpMethod: HttpMethod, + val path: String, + val payload: Optional>, + val userId: Optional, + val actionDescription: String +) + +abstract class ResourceCommand( + protected val restMso: RestMsoImplementation, + protected val inProgressStatusService: InProgressStatusService, + protected val msoResultHandlerService: MsoResultHandlerService, + protected val watchChildrenJobsBL: WatchChildrenJobsBL +) : CommandBase(), JobCommand { + + companion object { + private val Logger = EELFLoggerDelegate.getLogger(ResourceCommand::class.java) + } + + abstract fun createChildren():JobStatus + + abstract fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan + + abstract fun planDeleteMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan + + private val commandByInternalState: Map JobStatus> = hashMapOf( + Pair(InternalState.CREATING_CHILDREN, ::createChildren), + Pair(InternalState.WATCHING, ::watchChildren), + Pair(InternalState.CREATE_MYSELF, ::createMyself), + Pair(InternalState.DELETE_MYSELF, ::deleteMyself), + Pair(InternalState.IN_PROGRESS, ::inProgress) + ) + + private lateinit var internalState:InternalState + protected lateinit var actionPhase: Action + private var commandParentData: CommandParentData = CommandParentData() + private var msoResourceIds: MsoResourceIds = EMPTY_MSO_RESOURCE_ID + protected var childJobs:List = emptyList() + private lateinit var cumulativeStatus:JobStatus + + + override fun call(): NextCommand { + var jobStatus:JobStatus = invokeCommand() + jobStatus = comulateStatusAndUpdatePropertyIfFinal(jobStatus) + + Logger.debug("command for job ${sharedData.jobUuid} invoked and finished with jobStatus $jobStatus") + if (shallStopJob(jobStatus)) { + onFinal(jobStatus) + return NextCommand(jobStatus) + } + + val (nextActionPhase, nextInternalState) = calcNextInternalState(jobStatus, internalState, actionPhase) + Logger.debug("next state for job ${sharedData.jobUuid} is $nextInternalState") + actionPhase = nextActionPhase + internalState = nextInternalState + + if (internalState==InternalState.TERMINAL) { + onFinal(jobStatus) + return NextCommand(jobStatus) + } + + jobStatus = getExternalInProgressStatus() + Logger.debug("next status for job ${sharedData.jobUuid} is $jobStatus") +// if (internalState.immediate) return call() //shortcut instead of execute another command + return NextCommand(jobStatus, this) + } + + //we want to stop in faliures, except for service witn no action, since service with no action trigger 2 phases (delete and create) + protected fun shallStopJob(jobStatus: JobStatus) = + jobStatus.isFailure && !(isServiceCommand() && getActionType()==Action.None) + + //this method is used to expose the job status after successful completion of current state + //should be override by subclass (like ServiceCommand) that need to return other default job status + protected open fun getExternalInProgressStatus() = JobStatus.RESOURCE_IN_PROGRESS + + private fun invokeCommand(): JobStatus { + return commandByInternalState.getOrDefault (internalState, ::throwIllegalState).invoke() + } + + private fun throwIllegalState():JobStatus { + throw IllegalStateException("can't find action for pashe $actionPhase and state $internalState") + } + + private fun calcNextInternalState(jobStatus: JobStatus, internalState: InternalState, actionPhase: Action): NextInternalState { + + val nextInternalState = when (actionPhase) { + Action.Delete -> calcNextStateDeletePhase(jobStatus, internalState) + Action.Create -> calcNextStateCreatePhase(jobStatus, internalState) + else -> InternalState.TERMINAL + } + + if (nextInternalState == InternalState.TERMINAL + && actionPhase == Action.Delete + && isServiceCommand()) { + // Loop over to "Create" phase + return NextInternalState(Action.Create, InternalState.INITIAL) + } + + return NextInternalState(actionPhase, nextInternalState) + + } + + //no need to refer to failed (final) states here + //This method is called only for non final states or COMPLETED + protected fun calcNextStateDeletePhase(jobStatus: JobStatus, internalState: InternalState): InternalState { + return when (internalState) { + + InternalState.CREATING_CHILDREN -> InternalState.WATCHING + + InternalState.WATCHING -> { + when { + !jobStatus.isFinal -> InternalState.WATCHING + isNeedToDeleteMyself() -> InternalState.DELETE_MYSELF + else -> InternalState.TERMINAL + } + } + + InternalState.DELETE_MYSELF -> InternalState.IN_PROGRESS + + InternalState.IN_PROGRESS -> { + if (jobStatus == Job.JobStatus.COMPLETED) InternalState.TERMINAL else InternalState.IN_PROGRESS + } + + else -> InternalState.TERMINAL + } + } + + protected fun calcNextStateCreatePhase(jobStatus: JobStatus, internalState: InternalState): InternalState { + return when (internalState) { + + InternalState.CREATE_MYSELF -> InternalState.IN_PROGRESS + + InternalState.IN_PROGRESS -> { + if (jobStatus == Job.JobStatus.COMPLETED) InternalState.CREATING_CHILDREN else InternalState.IN_PROGRESS + } + + InternalState.CREATING_CHILDREN -> InternalState.WATCHING + + InternalState.WATCHING -> { + when { + !jobStatus.isFinal -> InternalState.WATCHING + else -> InternalState.TERMINAL + } + } + + + else -> InternalState.TERMINAL + } + } + + override fun getData(): Map { + return mapOf( + ACTION_PHASE to actionPhase, + INTERNAL_STATE to internalState, + MSO_RESOURCE_ID to msoResourceIds, + CHILD_JOBS to childJobs, + CUMULATIVE_STATUS to cumulativeStatus + ) + } + + override fun init(sharedData: JobSharedData, commandData: Map): ResourceCommand { + init(sharedData) + val resourceIdsRaw:Any? = commandData[MSO_RESOURCE_ID] + commandParentData.initParentData(commandData) + msoResourceIds = + if (resourceIdsRaw != null) JACKSON_OBJECT_MAPPER.convertValue(resourceIdsRaw) + else EMPTY_MSO_RESOURCE_ID + + childJobs = JACKSON_OBJECT_MAPPER.convertValue(commandData.getOrDefault(CHILD_JOBS, emptyList())) + cumulativeStatus = getEnumFromMapOfStrings(commandData, CUMULATIVE_STATUS, JobStatus.COMPLETED_WITH_NO_ACTION) + actionPhase = getEnumFromMapOfStrings(commandData, ACTION_PHASE, Action.Delete) + internalState = calcInitialState(commandData, actionPhase) + return this + } + + private fun calcInitialState(commandData: Map, phase: Action):InternalState { + val status:InternalState = getEnumFromMapOfStrings(commandData, INTERNAL_STATE, InternalState.INITIAL) + if (status == InternalState.INITIAL) { + onInitial(phase) + return when (phase) { + Action.Delete -> InternalState.CREATING_CHILDREN + Action.Create -> if (isNeedToCreateMyself()) InternalState.CREATE_MYSELF else InternalState.CREATING_CHILDREN + else -> throw IllegalStateException("state $internalState is not supported yet") + } + } + return status + } + + //command may override it in order to do something while init state + protected open fun onInitial(phase: Action) { + //do nothing + } + + //command may override it in order to do something while final status + protected open fun onFinal(jobStatus: JobStatus) { + //do nothing + } + + protected open fun getRequest(): BaseResource { + return sharedData.request as BaseResource + } + + protected open fun getActionType(): Action { + return getRequest().action + } + + protected open fun isServiceCommand(): Boolean = false + + protected open fun isNeedToDeleteMyself(): Boolean = getActionType() == Action.Delete + + protected open fun isNeedToCreateMyself(): Boolean = getActionType() == Action.Create + + protected open fun inProgress(): Job.JobStatus { + val requestId:String = msoResourceIds.requestId; + return try { + val jobStatus = inProgressStatusService.call(getExpiryChecker(), sharedData, requestId) + handleInProgressStatus(jobStatus) + } catch (e: javax.ws.rs.ProcessingException) { + // Retry when we can't connect MSO during getStatus + Logger.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, will retry: {}", requestId, e, e) + Job.JobStatus.IN_PROGRESS; + } catch (e: InProgressStatusService.BadResponseFromMso) { + inProgressStatusService.handleFailedMsoResponse(sharedData.jobUuid, requestId, e.msoResponse) + Job.JobStatus.IN_PROGRESS + } catch (e: RuntimeException) { + Logger.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, stopping: {}", requestId, e, e) + Job.JobStatus.STOPPED + } + } + + fun createMyself(): Job.JobStatus { + val createMyselfCommand = planCreateMyselfRestCall(commandParentData, sharedData.request, sharedData.userId) + + return executeAndHandleMsoInstanceRequest(createMyselfCommand) + } + + fun deleteMyself(): Job.JobStatus { + val deleteMyselfCommand = planDeleteMyselfRestCall(commandParentData, sharedData.request, sharedData.userId) + + return executeAndHandleMsoInstanceRequest(deleteMyselfCommand) + } + + private fun executeAndHandleMsoInstanceRequest(restCallPlan: MsoRestCallPlan): JobStatus { + val msoResponse = restMso.restCall( + restCallPlan.httpMethod, + RequestReferencesContainer::class.java, + restCallPlan.payload.orElse(null), + restCallPlan.path, + restCallPlan.userId + ) + + val msoResult = if (isServiceCommand()) { + msoResultHandlerService.handleRootResponse(sharedData.jobUuid, msoResponse) + } else { + msoResultHandlerService.handleResponse(msoResponse, restCallPlan.actionDescription) + } + + this.msoResourceIds = msoResult.msoResourceIds + return msoResult.jobStatus + } + + protected open fun getExpiryChecker(): ExpiryChecker = ExpiryChecker {false} + + protected open fun handleInProgressStatus(jobStatus: JobStatus): JobStatus { + return if (jobStatus == Job.JobStatus.PAUSE) Job.JobStatus.IN_PROGRESS else jobStatus + } + + protected open fun watchChildren():JobStatus { + return watchChildrenJobsBL.retrieveChildrenJobsStatus(childJobs) + } + + private fun comulateStatusAndUpdatePropertyIfFinal(internalStateStatus: JobStatus): JobStatus { + val status = watchChildrenJobsBL.cumulateJobStatus(internalStateStatus, cumulativeStatus) + + //we want to update cumulativeStatus only for final status + if (status.isFinal) { + cumulativeStatus = status; + } + + return status + } +} + + + diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceInProgressStatusCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceInProgressStatusCommand.java new file mode 100644 index 000000000..9ab600aae --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceInProgressStatusCommand.java @@ -0,0 +1,32 @@ +package org.onap.vid.job.command; + +import org.onap.vid.job.Job; +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.impl.JobSharedData; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class ResourceInProgressStatusCommand extends BaseInProgressStatusCommand { + + public ResourceInProgressStatusCommand() { + } + + ResourceInProgressStatusCommand(JobSharedData sharedData, String requestId, String instanceId) { + init(sharedData, requestId, instanceId); + } + + @Override + protected ExpiryChecker getExpiryChecker() { + return x->false; + } + + @Override + protected NextCommand processJobStatus(Job.JobStatus jobStatus) { + return new NextCommand(jobStatus, this); + } + + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceInstantiationCommand.java new file mode 100644 index 000000000..c86c6f1be --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceInstantiationCommand.java @@ -0,0 +1,87 @@ +package org.onap.vid.job.command; + +import org.onap.vid.changeManagement.RequestDetailsWrapper; +import org.onap.vid.job.Job; +import org.onap.vid.job.JobAdapter; +import org.onap.vid.job.JobCommand; +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.impl.JobSharedData; +import org.onap.vid.model.RequestReferencesContainer; +import org.onap.vid.mso.RestMsoImplementation; +import org.onap.vid.mso.RestObject; +import org.onap.vid.mso.model.BaseResourceInstantiationRequestDetails; +import org.onap.vid.services.AsyncInstantiationBusinessLogic; +import org.onap.vid.services.AuditService; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.Map; + + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public abstract class ResourceInstantiationCommand extends BaseInstantiationCommand implements JobCommand { + + + @Inject + protected RestMsoImplementation restMso; + + @Inject + private AsyncInstantiationBusinessLogic asyncInstantiationBL; + + @Inject + private AuditService auditService; + + @Override + public ResourceInstantiationCommand init(JobSharedData sharedData, Map commandData) { + super.init(sharedData, commandData); + return this; + } + + @Override + public Map getData() { + return commandParentData.getParentData(); + } + + @Override + public NextCommand call() { + if (!shouldInstantiateMyself()) { + return new NextCommand(Job.JobStatus.COMPLETED_WITH_NO_ACTION); + } + + RequestDetailsWrapper requestDetailsWrapper = generateMSORequest( + getSharedData().getRequest(), + getSharedData().getUserId() + ); + String instantiatePath = getRequestPath(); + + RestObject msoResponse = restMso.PostForObject(requestDetailsWrapper, + instantiatePath, RequestReferencesContainer.class); + + if (msoResponse.getStatusCode() >= 200 && msoResponse.getStatusCode() < 400) { + String requestId = msoResponse.get().getRequestReferences().getRequestId(); + String instanceId = msoResponse.get().getRequestReferences().getInstanceId(); + asyncInstantiationBL.auditMsoStatus(getSharedData().getRootJobId(), getJobAuditMSOStatus(), requestId, null); + return getNextCommand(requestId, instanceId); + } + else { + auditService.setFailedAuditStatusFromMso(getSharedData().getRootJobId(), null, msoResponse.getStatusCode(), msoResponse.getRaw()); + return new NextCommand(Job.JobStatus.FAILED); + } + } + protected NextCommand getNextCommand(String requestId, String instanceId){ + return new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, new ResourceInProgressStatusCommand(getSharedData(), requestId, instanceId)); + } + + protected boolean shouldInstantiateMyself() { + return true; + } + + protected abstract String getRequestPath(); + protected abstract RequestDetailsWrapper generateMSORequest(JobAdapter.AsyncJobRequest request, String userId); + protected abstract String getJobAuditMSOStatus(); +} + + diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceWithChildrenInProgressCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceWithChildrenInProgressCommand.java new file mode 100644 index 000000000..53164f433 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ResourceWithChildrenInProgressCommand.java @@ -0,0 +1,65 @@ +package org.onap.vid.job.command; + +import org.onap.vid.job.Job; +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.impl.JobSharedData; + +import java.util.HashMap; +import java.util.Map; + + +public class ResourceWithChildrenInProgressCommand extends BaseInProgressStatusCommand { + + public ResourceWithChildrenInProgressCommand() { + } + + public ResourceWithChildrenInProgressCommand(JobSharedData sharedData, + String requestId, + String instanceId, + CommandParentData commandParentData) { + init(sharedData, requestId, instanceId, commandParentData); + } + + protected BaseInProgressStatusCommand init(JobSharedData sharedData, + String requestId, + String instanceId, + CommandParentData commandParentData) { + init(sharedData, requestId, instanceId); + this.commandParentData= commandParentData; + return this; + } + + + @Override + public Map getData() { + Map data = new HashMap<>(super.getData()); + data.putAll(buildDataForChild()); + return data; + } + + @Override + public BaseInProgressStatusCommand init(JobSharedData sharedData, Map commandData) { + return init( + sharedData, + (String) commandData.get("requestId"), + (String) commandData.get("instanceId"), + commandParentData.initParentData(commandData)); + } + + protected Map buildDataForChild() { + return commandParentData.getParentData(); + } + + + + @Override + protected NextCommand processJobStatus(Job.JobStatus jobStatus) { + return new NextCommand(jobStatus, this); + } + + @Override + protected ExpiryChecker getExpiryChecker() { + return x->false; + } + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ServiceInProgressStatusCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/ServiceInProgressStatusCommand.java new file mode 100644 index 000000000..ac8b5546f --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ServiceInProgressStatusCommand.java @@ -0,0 +1,100 @@ +package org.onap.vid.job.command; + +import org.apache.commons.collections.MapUtils; +import org.onap.vid.job.Job; +import org.onap.vid.job.Job.JobStatus; +import org.onap.vid.job.JobCommand; +import org.onap.vid.job.JobType; +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.command.CommandParentData.CommandDataKey; +import org.onap.vid.job.impl.JobSharedData; +import org.onap.vid.model.serviceInstantiation.ServiceInstantiation; +import org.onap.vid.properties.Features; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class ServiceInProgressStatusCommand extends BaseInProgressStatusCommand { + + public ServiceInProgressStatusCommand() { + } + + ServiceInProgressStatusCommand(JobSharedData sharedData, MsoResourceIds msoResourceIds) { + init(sharedData, msoResourceIds.getRequestId(), msoResourceIds.getInstanceId()); + } + + @Override + protected ExpiryChecker getExpiryChecker() { + return new ServiceExpiryChecker(); + } + + protected NextCommand processJobStatus(Job.JobStatus jobStatus) { + JobCommand jobCommand = this; + Job.JobStatus nextJobStatus = jobStatus; + switch (jobStatus) { + case FAILED: + asyncInstantiationBL.handleFailedInstantiation(getSharedData().getJobUuid()); + return new NextCommand(nextJobStatus, jobCommand); + case PAUSE: + nextJobStatus = Job.JobStatus.IN_PROGRESS; + break; + case COMPLETED: + ServiceInstantiation request = (ServiceInstantiation) getSharedData().getRequest(); + if (isNeedToCreateChildJobs(request)) { + List childrenJobs = getChildJobs(request); + nextJobStatus = Job.JobStatus.IN_PROGRESS; + jobCommand = new WatchingCommand(getSharedData(), childrenJobs, true); + return new NextCommand(nextJobStatus, jobCommand); + } + break; + default: // for sonar + } + asyncInstantiationBL.updateServiceInfoAndAuditStatus(getSharedData().getJobUuid(), jobStatus); + return new NextCommand(nextJobStatus, jobCommand); + } + + private List getChildJobs(ServiceInstantiation request) { + Map dataForChild = buildDataForChild(request); + + Stream vnfJobs = request.getVnfs().values().stream().map( + vnf -> jobsBrokerService.add( + jobAdapter.createChildJob(JobType.VnfInstantiation, JobStatus.CREATING , vnf, getSharedData(), dataForChild)).toString() + ); + + Stream networkJobs = request.getNetworks().values().stream().map( + network -> jobsBrokerService.add( + jobAdapter.createChildJob(JobType.NetworkInstantiation, JobStatus.CREATING , network, getSharedData(), dataForChild)).toString() + ); + + Stream instanceGroupJobs = request.getVnfGroups().values().stream().map( + instanceGroup -> jobsBrokerService.add( + jobAdapter.createChildJob(JobType.InstanceGroupInstantiation, JobStatus.CREATING , instanceGroup, getSharedData(), dataForChild)).toString() + ); + + return Stream.of(vnfJobs, networkJobs, instanceGroupJobs) + .reduce(Stream::concat) + .orElseGet(Stream::empty) + .collect(Collectors.toList()); + } + + public boolean isNeedToCreateChildJobs(ServiceInstantiation request) { + return featureManager.isActive(Features.FLAG_ASYNC_ALACARTE_VNF) && request.isALaCarte() && + ( + MapUtils.isNotEmpty(request.getVnfs()) || MapUtils.isNotEmpty(request.getNetworks()) || + (featureManager.isActive(Features.FLAG_1902_VNF_GROUPING) && MapUtils.isNotEmpty(request.getVnfGroups())) + ); + } + + protected Map buildDataForChild(ServiceInstantiation request) { + commandParentData.addInstanceId(CommandDataKey.SERVICE_INSTANCE_ID, this.instanceId); + commandParentData.addModelInfo(CommandDataKey.SERVICE_MODEL_INFO, request.getModelInfo()); + return commandParentData.getParentData(); + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/ServiceInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/ServiceInstantiationCommand.java index 958fc115e..b40b7015c 100644 --- a/vid-app-common/src/main/java/org/onap/vid/job/command/ServiceInstantiationCommand.java +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/ServiceInstantiationCommand.java @@ -1,170 +1,106 @@ -/*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2018 Nokia. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ package org.onap.vid.job.command; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; -import io.joshworks.restclient.http.HttpResponse; -import org.onap.vid.aai.exceptions.InvalidAAIResponseException; +import org.apache.commons.lang3.ObjectUtils; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.vid.aai.ExceptionWithRequestInfo; import org.onap.vid.changeManagement.RequestDetailsWrapper; import org.onap.vid.exceptions.MaxRetriesException; import org.onap.vid.job.Job; import org.onap.vid.job.JobCommand; import org.onap.vid.job.NextCommand; +import org.onap.vid.job.impl.JobSharedData; import org.onap.vid.model.RequestReferencesContainer; import org.onap.vid.model.serviceInstantiation.ServiceInstantiation; -import org.onap.vid.mso.MsoInterface; +import org.onap.vid.mso.RestMsoImplementation; +import org.onap.vid.mso.RestObject; import org.onap.vid.mso.model.ServiceInstantiationRequestDetails; import org.onap.vid.services.AsyncInstantiationBusinessLogic; -import org.onap.vid.services.AuditService; -import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; import javax.inject.Inject; import java.util.Map; -import java.util.Objects; -import java.util.UUID; -@Component -@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) -public class ServiceInstantiationCommand implements JobCommand { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); +public abstract class ServiceInstantiationCommand extends BaseRootCommand implements JobCommand { private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(ServiceInstantiationCommand.class); @Inject - private AsyncInstantiationBusinessLogic asyncInstantiationBL; - - @Inject - private AuditService auditService; + protected AsyncInstantiationBusinessLogic asyncInstantiationBL; @Inject - private MsoInterface restMso; + private RestMsoImplementation restMso; - private UUID uuid; - private ServiceInstantiation serviceInstantiationRequest; - private String userId; + protected String optimisticUniqueServiceInstanceName; public ServiceInstantiationCommand() { } - public ServiceInstantiationCommand(UUID uuid, ServiceInstantiation serviceInstantiationRequest, String userId) { - init(uuid, serviceInstantiationRequest, userId); - } - - ServiceInstantiationCommand(AsyncInstantiationBusinessLogic asyncInstantiationBL, AuditService auditService, MsoInterface msoInterface, - UUID uuid, ServiceInstantiation serviceInstantiation, String userId) { - this(uuid, serviceInstantiation, userId); - this.asyncInstantiationBL = asyncInstantiationBL; - this.auditService = auditService; - this.restMso = msoInterface; - } - @Override public NextCommand call() { RequestDetailsWrapper requestDetailsWrapper ; try { - requestDetailsWrapper = asyncInstantiationBL.generateServiceInstantiationRequest( - uuid, serviceInstantiationRequest, userId - ); + requestDetailsWrapper = generateServiceInstantiationRequest(); } + //Aai return bad response while checking names uniqueness - catch (InvalidAAIResponseException exception) { - LOGGER.error("Failed to check name uniqueness in AAI. VID will try again later", exception); - //put the job in_progress so we will keep trying to check name uniqueness in AAI - //And then send the request to MSO - return new NextCommand(Job.JobStatus.IN_PROGRESS, this); + catch (ExceptionWithRequestInfo exception) { + return handleAaiNameUniquenessBadResponse(exception); } //Vid reached to max retries while trying to find unique name in AAI catch (MaxRetriesException exception) { - LOGGER.error("Failed to find unused name in AAI. Set the job to FAILED ", exception); - return handleCommandFailed(); + return handleMaxRetryInNameUniqueness(exception); } - String path = asyncInstantiationBL.getServiceInstantiationPath(serviceInstantiationRequest); + String path = asyncInstantiationBL.getServiceInstantiationPath(getRequest()); - HttpResponse msoResponse = restMso.post(path, - requestDetailsWrapper, RequestReferencesContainer.class); + RestObject msoResponse = restMso.PostForObject(requestDetailsWrapper, + path, RequestReferencesContainer.class); + return handleRootResponse(msoResponse); - if (msoResponse.getStatus() >= 200 && msoResponse.getStatus() < 400) { - final Job.JobStatus jobStatus = Job.JobStatus.IN_PROGRESS; - final String requestId = msoResponse.getBody().getRequestReferences().getRequestId(); - final String instanceId = msoResponse.getBody().getRequestReferences().getInstanceId(); - asyncInstantiationBL.auditVidStatus(uuid, jobStatus); - setInitialRequestAuditStatusFromMso(requestId); - asyncInstantiationBL.updateServiceInfo(uuid, x-> { - x.setJobStatus(jobStatus); - x.setServiceInstanceId(instanceId); - }); - - return new NextCommand(jobStatus, new InProgressStatusCommand(uuid, requestId)); - } else { - auditService.setFailedAuditStatusFromMso(uuid,null, msoResponse.getStatus(), - Objects.toString(msoResponse.getBody())); - return handleCommandFailed(); - } + } + @Override + protected ServiceInstantiation getRequest() { + return (ServiceInstantiation) getSharedData().getRequest(); } - private void setInitialRequestAuditStatusFromMso(String requestId){ - final String initialMsoRequestStatus = "REQUESTED"; - asyncInstantiationBL.auditMsoStatus(uuid,initialMsoRequestStatus,requestId,null); + protected abstract RequestDetailsWrapper generateServiceInstantiationRequest(); + + private NextCommand handleMaxRetryInNameUniqueness(MaxRetriesException exception) { + LOGGER.error("Failed to find unused name in AAI. Set the job to FAILED ", exception); + return handleCommandFailed(); } - protected NextCommand handleCommandFailed() { - asyncInstantiationBL.handleFailedInstantiation(uuid); - return new NextCommand(Job.JobStatus.FAILED); + private NextCommand handleAaiNameUniquenessBadResponse(ExceptionWithRequestInfo exception) { + LOGGER.error("Failed to check name uniqueness in AAI. VID will try again later", exception); + //put the job in_progress so we will keep trying to check name uniqueness in AAI + //And then send the request to MSO + return new NextCommand(Job.JobStatus.IN_PROGRESS, this); } @Override - public ServiceInstantiationCommand init(UUID jobUuid, Map data) { - final Object request = data.get("request"); + public ServiceInstantiationCommand init(JobSharedData sharedData, Map commandData) { return init( - jobUuid, - OBJECT_MAPPER.convertValue(request, ServiceInstantiation.class), - (String) data.get("userId") + sharedData, + (String) commandData.get("optimisticUniqueServiceInstanceName") ); } - private ServiceInstantiationCommand init(UUID jobUuid, ServiceInstantiation serviceInstantiationRequest, String userId) { - this.uuid = jobUuid; - this.serviceInstantiationRequest = serviceInstantiationRequest; - this.userId = userId; - + protected ServiceInstantiationCommand init(JobSharedData sharedData, String optimisticUniqueServiceInstanceName) { + init(sharedData); + this.optimisticUniqueServiceInstanceName = ObjectUtils.defaultIfNull(optimisticUniqueServiceInstanceName, + (getRequest()).getInstanceName()); return this; } @Override public Map getData() { return ImmutableMap.of( - "uuid", uuid, - "request", serviceInstantiationRequest, - "userId", userId + "optimisticUniqueServiceInstanceName", optimisticUniqueServiceInstanceName ); } } diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/VfmoduleInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/VfmoduleInstantiationCommand.java new file mode 100644 index 000000000..a3f827e2f --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/VfmoduleInstantiationCommand.java @@ -0,0 +1,44 @@ +package org.onap.vid.job.command; + +import org.onap.vid.changeManagement.RequestDetailsWrapper; +import org.onap.vid.job.JobAdapter; +import org.onap.vid.job.command.CommandParentData.CommandDataKey; +import org.onap.vid.model.serviceInstantiation.VfModule; +import org.onap.vid.mso.model.VfModuleInstantiationRequestDetails; +import org.onap.vid.services.AsyncInstantiationBusinessLogic; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class VfmoduleInstantiationCommand extends ResourceInstantiationCommand { + @Inject + private AsyncInstantiationBusinessLogic asyncInstantiationBL; + + @Override + protected String getRequestPath() { + return asyncInstantiationBL.getVfmoduleInstantiationPath(commandParentData.getInstanceId(CommandDataKey.SERVICE_INSTANCE_ID),commandParentData.getInstanceId(CommandDataKey.VNF_INSTANCE_ID)); + } + + @Override + protected RequestDetailsWrapper generateMSORequest(JobAdapter.AsyncJobRequest request, String userId) { + return asyncInstantiationBL.generateVfModuleInstantiationRequest( + (VfModule) getSharedData().getRequest(), + commandParentData.getModelInfo(CommandDataKey.SERVICE_MODEL_INFO), + commandParentData.getInstanceId(CommandDataKey.SERVICE_INSTANCE_ID), + commandParentData.getModelInfo(CommandDataKey.VNF_MODEL_INFO), + commandParentData.getInstanceId(CommandDataKey.VNF_INSTANCE_ID), + commandParentData.getInstanceId(CommandDataKey.VG_INSTANCE_ID), + getSharedData().getUserId() + ); + } + + @Override + protected String getJobAuditMSOStatus() { + return "VF_MODULE_REQUESTED"; + } + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/VnfInProgressStatusCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/VnfInProgressStatusCommand.java new file mode 100644 index 000000000..a6a41b1ae --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/VnfInProgressStatusCommand.java @@ -0,0 +1,87 @@ +package org.onap.vid.job.command; + +import org.apache.commons.collections.MapUtils; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.vid.asdc.AsdcCatalogException; +import org.onap.vid.job.Job; +import org.onap.vid.job.JobType; +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.command.CommandParentData.CommandDataKey; +import org.onap.vid.job.impl.JobSharedData; +import org.onap.vid.model.serviceInstantiation.BaseResource; +import org.onap.vid.model.serviceInstantiation.VfModule; +import org.onap.vid.model.serviceInstantiation.Vnf; +import org.onap.vid.properties.Features; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class VnfInProgressStatusCommand extends ResourceWithChildrenInProgressCommand { + private static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(VnfInProgressStatusCommand.class); + + public VnfInProgressStatusCommand(JobSharedData sharedData, + String requestId, + String instanceId, + CommandParentData commandParentData) { + super(sharedData, requestId, instanceId, commandParentData); + } + + public VnfInProgressStatusCommand() { + } + + @Override + protected NextCommand processJobStatus(Job.JobStatus jobStatus) { + if (jobStatus == Job.JobStatus.FAILED) { + return new NextCommand(jobStatus); + } + + Vnf request = (Vnf) getSharedData().getRequest(); + + if (isNeedToCreateChildJobs(jobStatus, request)) { + commandParentData.addInstanceId(CommandDataKey.VNF_INSTANCE_ID, instanceId); + commandParentData.addModelInfo(CommandDataKey.VNF_MODEL_INFO, request.getModelInfo()); + //create volume group of base module job + Map dataForChild = buildDataForChild(); + List vfModules = request.getVfModules().values().stream().flatMap(vfKey -> vfKey.values().stream()).collect(Collectors.toList()); + List vgBaseJobs = new ArrayList<>(); + for( VfModule vfModule : vfModules){ + try { + if(commandUtils.isVfModuleBaseModule(commandParentData.getModelInfo(CommandDataKey.SERVICE_MODEL_INFO).getModelVersionId(), vfModule.getModelInfo().getModelVersionId())) { + vgBaseJobs.add(jobsBrokerService.add( + jobAdapter.createChildJob(JobType.VolumeGroupInstantiation, Job.JobStatus.CREATING, vfModule, getSharedData(), dataForChild)).toString()); + } + } catch (AsdcCatalogException e) { + LOG.error("Failed to retrieve service definitions from SDC, for VfModule is BaseModule. Error: "+e.getMessage() , e); + return new NextCommand(Job.JobStatus.COMPLETED_WITH_ERRORS); + } + } + return new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, new WatchingCommandBaseModule(getSharedData(), vgBaseJobs, false, commandParentData)); + } + + //in case of JobStatus.PAUSE we leave the job itself as IN_PROGRESS, for keep tracking job progress + if (jobStatus == Job.JobStatus.PAUSE) { + return new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, this); + } + return new NextCommand(jobStatus, this); + } + + + protected boolean isNeedToCreateChildJobs(Job.JobStatus jobStatus, BaseResource request) { + return featureManager.isActive(Features.FLAG_ASYNC_ALACARTE_VFMODULE) && + jobStatus == Job.JobStatus.COMPLETED && + MapUtils.isNotEmpty(((Vnf)request).getVfModules()); + } + + + @Override + protected ExpiryChecker getExpiryChecker() { + return x->false; + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/VnfInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/VnfInstantiationCommand.java new file mode 100644 index 000000000..8853cdc14 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/VnfInstantiationCommand.java @@ -0,0 +1,50 @@ +package org.onap.vid.job.command; + +import org.onap.vid.changeManagement.RequestDetailsWrapper; +import org.onap.vid.job.Job; +import org.onap.vid.job.JobAdapter; +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.command.CommandParentData.CommandDataKey; +import org.onap.vid.model.serviceInstantiation.Vnf; +import org.onap.vid.mso.model.VnfInstantiationRequestDetails; +import org.onap.vid.services.AsyncInstantiationBusinessLogic; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class VnfInstantiationCommand extends ResourceInstantiationCommand { + + @Inject + private AsyncInstantiationBusinessLogic asyncInstantiationBL; + + @Override + protected String getRequestPath() { + return asyncInstantiationBL.getVnfInstantiationPath( commandParentData.getInstanceId(CommandDataKey.SERVICE_INSTANCE_ID)); + } + + @Override + protected RequestDetailsWrapper generateMSORequest(JobAdapter.AsyncJobRequest request, String userId) { + return asyncInstantiationBL.generateVnfInstantiationRequest( + (Vnf) getSharedData().getRequest(), + commandParentData.getModelInfo(CommandDataKey.SERVICE_MODEL_INFO), + commandParentData.getInstanceId(CommandDataKey.SERVICE_INSTANCE_ID), + getSharedData().getUserId() + ); + } + + @Override + protected String getJobAuditMSOStatus() { + return "VNF_REQUESTED"; + } + + @Override + protected NextCommand getNextCommand(String requestId, String instanceId) { + return new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, + new VnfInProgressStatusCommand(getSharedData(), requestId, instanceId, commandParentData)); + } + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupInProgressStatusCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupInProgressStatusCommand.java new file mode 100644 index 000000000..663696b32 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupInProgressStatusCommand.java @@ -0,0 +1,65 @@ +package org.onap.vid.job.command; + +import org.onap.vid.job.Job; +import org.onap.vid.job.JobType; +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.command.CommandParentData.CommandDataKey; +import org.onap.vid.job.impl.JobSharedData; +import org.onap.vid.model.serviceInstantiation.VfModule; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class VolumeGroupInProgressStatusCommand extends ResourceWithChildrenInProgressCommand { + + public VolumeGroupInProgressStatusCommand( + JobSharedData sharedData, + String requestId, + String instanceId, + CommandParentData parentData) { + super(sharedData, requestId, instanceId, parentData); + } + + public VolumeGroupInProgressStatusCommand() { + } + + @Override + protected NextCommand processJobStatus(Job.JobStatus jobStatus) { + if (jobStatus == Job.JobStatus.FAILED) { + return new NextCommand(Job.JobStatus.FAILED); + } + VfModule request = (VfModule) getSharedData().getRequest(); + + if (jobStatus == Job.JobStatus.COMPLETED) { + //vf module creation + Map dataForChild = buildDataForChild(); + List vfModuleJob = Arrays.asList(jobsBrokerService.add( + jobAdapter.createChildJob(JobType.VfmoduleInstantiation, Job.JobStatus.CREATING , request, getSharedData(), dataForChild)).toString()); + + return new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, new WatchingCommand(getSharedData(), vfModuleJob, false)); + } + + //in case of JobStatus.PAUSE we leave the job itself as IN_PROGRESS, for keep tracking job progress + if (jobStatus == Job.JobStatus.PAUSE) { + return new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, this); + } + return new NextCommand(jobStatus, this); + } + + @Override + protected Map buildDataForChild() { + commandParentData.addInstanceId(CommandDataKey.VG_INSTANCE_ID, this.instanceId); + return super.buildDataForChild(); + } + + @Override + protected ExpiryChecker getExpiryChecker() { + return x->false; + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupInstantiationCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupInstantiationCommand.java new file mode 100644 index 000000000..b2a3da472 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/VolumeGroupInstantiationCommand.java @@ -0,0 +1,83 @@ +package org.onap.vid.job.command; + + +import org.apache.commons.lang3.StringUtils; +import org.onap.vid.changeManagement.RequestDetailsWrapper; +import org.onap.vid.job.*; +import org.onap.vid.job.command.CommandParentData.CommandDataKey; +import org.onap.vid.model.serviceInstantiation.VfModule; +import org.onap.vid.mso.model.VolumeGroupRequestDetails; +import org.onap.vid.services.AsyncInstantiationBusinessLogic; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class VolumeGroupInstantiationCommand extends ResourceInstantiationCommand { + @Inject + private AsyncInstantiationBusinessLogic asyncInstantiationBL; + + @Inject + protected JobsBrokerService jobsBrokerService; + + @Inject + protected JobAdapter jobAdapter; + + @Override + protected String getRequestPath() { + return asyncInstantiationBL.getVolumeGroupInstantiationPath(commandParentData.getInstanceId(CommandDataKey.SERVICE_INSTANCE_ID),commandParentData.getInstanceId(CommandDataKey.VNF_INSTANCE_ID)); + } + + @Override + protected RequestDetailsWrapper generateMSORequest(JobAdapter.AsyncJobRequest request, String userId) { + return asyncInstantiationBL.generateVolumeGroupInstantiationRequest( + (VfModule) getSharedData().getRequest(), + commandParentData.getModelInfo(CommandDataKey.SERVICE_MODEL_INFO), + commandParentData.getInstanceId(CommandDataKey.SERVICE_INSTANCE_ID), + commandParentData.getModelInfo(CommandDataKey.VNF_MODEL_INFO), + commandParentData.getInstanceId(CommandDataKey.VNF_INSTANCE_ID), + getSharedData().getUserId() + ); + } + + @Override + protected NextCommand getNextCommand(String requestId, String instanceId){ + return new NextCommand( + Job.JobStatus.RESOURCE_IN_PROGRESS, + new VolumeGroupInProgressStatusCommand(getSharedData(), requestId, instanceId, commandParentData) + ); + } + + @Override + protected String getJobAuditMSOStatus() { + return "VOLUME_GROUP_REQUESTED"; + } + + @Override + public NextCommand call() { + String vgName = ((VfModule)getSharedData().getRequest()).getVolumeGroupInstanceName(); + if(StringUtils.isNotEmpty(vgName)){ + return super.call();//create volume group + }else { + //go to vf module creation + VfModule request = (VfModule) getSharedData().getRequest(); + Map dataForChild = buildDataForChild(); + List vfModuleJob = Collections.singletonList(jobsBrokerService.add( + jobAdapter.createChildJob(JobType.VfmoduleInstantiation, Job.JobStatus.CREATING, request, getSharedData(), dataForChild)).toString()); + + return new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, new WatchingCommand(getSharedData(), vfModuleJob, false)); + } + + } + + private Map buildDataForChild() { + return commandParentData.getParentData(); + } + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/WatchChildrenJobsBL.kt b/vid-app-common/src/main/java/org/onap/vid/job/command/WatchChildrenJobsBL.kt new file mode 100644 index 000000000..b31e7f92e --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/WatchChildrenJobsBL.kt @@ -0,0 +1,61 @@ +package org.onap.vid.job.command + +import org.apache.commons.lang3.StringUtils +import org.onap.portalsdk.core.service.DataAccessService +import org.onap.vid.job.Job +import org.onap.vid.job.Job.JobStatus.* +import org.onap.vid.job.impl.JobDaoImpl +import org.onap.vid.utils.DaoUtils +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.stereotype.Service +import java.util.* +import java.util.stream.Collectors +import java.util.stream.Stream + + +@Service +class WatchChildrenJobsBL @Autowired +constructor(private val dataAccessService: DataAccessService) { + + fun retrieveChildrenJobsStatus(childrenJobsIds: List): Job.JobStatus { + val jobs = getAllChildrenJobs(childrenJobsIds) + + val jobsStatuses = childrenJobsIds.stream() + .map { jobId -> jobs[UUID.fromString(jobId)] } + .map {when { + (it == null || it.status == null) -> Job.JobStatus.FAILED + else -> it.status + }} + + return cumulateJobStatus(jobsStatuses) + + } + + fun cumulateJobStatus(childrenComulatedStatus: Job.JobStatus, fatherJobStatus: Job.JobStatus): Job.JobStatus { + return cumulateJobStatus(Stream.of(childrenComulatedStatus, fatherJobStatus)) + } + + private fun cumulateJobStatus(jobsStatuses: Stream): Job.JobStatus { + + return jobsStatuses.reduce{ a, b -> + when { + !a.isFinal || !b.isFinal -> IN_PROGRESS + a == COMPLETED_WITH_ERRORS || b == COMPLETED_WITH_ERRORS-> COMPLETED_WITH_ERRORS + a == COMPLETED && b.isFailure -> COMPLETED_WITH_ERRORS + b == COMPLETED && a.isFailure -> COMPLETED_WITH_ERRORS + a == COMPLETED || b == COMPLETED -> COMPLETED + a.isFailure || b.isFailure -> FAILED + else -> COMPLETED_WITH_NO_ACTION + } + } .orElse(COMPLETED_WITH_NO_ACTION) + } + + private fun getAllChildrenJobs(childrenJobsIds: List): Map { + val jobs:MutableList = dataAccessService.getList(JobDaoImpl::class.java, filterByJobIds(childrenJobsIds), null, DaoUtils.getPropsMap()) as MutableList + return jobs.stream().collect(Collectors.toMap( { it.uuid }, { it })) + } + + private fun filterByJobIds(childrenJobsIds: List): String { + return " WHERE JOB_ID IN('" + StringUtils.join(childrenJobsIds, "', '") + "')" + } +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/WatchingCommand.java b/vid-app-common/src/main/java/org/onap/vid/job/command/WatchingCommand.java new file mode 100644 index 000000000..8659cfe1a --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/WatchingCommand.java @@ -0,0 +1,33 @@ +package org.onap.vid.job.command; + +import org.onap.vid.job.Job; +import org.onap.vid.job.NextCommand; +import org.onap.vid.job.impl.JobSharedData; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class WatchingCommand extends BaseWatchingCommand { + + public WatchingCommand() {} + + public WatchingCommand(JobSharedData sharedData, List childrenJobsIds, boolean isService) { + super(sharedData, childrenJobsIds, isService); + } + + protected NextCommand getNextCommand(Job.JobStatus cumulativeJobsStatus) { + if (cumulativeJobsStatus==Job.JobStatus.IN_PROGRESS) { + return (isService) ? new NextCommand(Job.JobStatus.IN_PROGRESS, this) + : new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, this); + } + if (isService) { + asyncInstantiationBL.updateServiceInfoAndAuditStatus(getSharedData().getJobUuid(), cumulativeJobsStatus); + } + return new NextCommand(cumulativeJobsStatus); + } + +} diff --git a/vid-app-common/src/main/java/org/onap/vid/job/command/WatchingCommandBaseModule.java b/vid-app-common/src/main/java/org/onap/vid/job/command/WatchingCommandBaseModule.java new file mode 100644 index 000000000..8012aa9f3 --- /dev/null +++ b/vid-app-common/src/main/java/org/onap/vid/job/command/WatchingCommandBaseModule.java @@ -0,0 +1,93 @@ +package org.onap.vid.job.command; + +import com.google.common.collect.ImmutableMap; +import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.vid.asdc.AsdcCatalogException; +import org.onap.vid.job.*; +import org.onap.vid.job.command.CommandParentData.CommandDataKey; +import org.onap.vid.job.impl.JobSharedData; +import org.onap.vid.model.serviceInstantiation.VfModule; +import org.onap.vid.model.serviceInstantiation.Vnf; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class WatchingCommandBaseModule extends BaseWatchingCommand { + @Inject + protected JobsBrokerService jobsBrokerService; + + @Inject + protected JobAdapter jobAdapter; + private static final EELFLoggerDelegate LOG = EELFLoggerDelegate.getLogger(WatchingCommandBaseModule.class); + + public WatchingCommandBaseModule( + JobSharedData sharedData, + List childrenJobsIds, + boolean isService, + CommandParentData commandParentData) { + super(sharedData, childrenJobsIds, isService); + this.commandParentData = commandParentData; + } + + public WatchingCommandBaseModule() { + + } + + @Override + protected NextCommand getNextCommand(Job.JobStatus cumulativeJobsStatus) { + + if (cumulativeJobsStatus== Job.JobStatus.IN_PROGRESS) { + return new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, this); + } + + if(cumulativeJobsStatus==Job.JobStatus.FAILED || cumulativeJobsStatus==Job.JobStatus.COMPLETED_WITH_ERRORS){ + return new NextCommand(Job.JobStatus.COMPLETED_WITH_ERRORS); + } + Vnf request = (Vnf) getSharedData().getRequest(); + Map dataForChild = buildDataForChild(); + //Create non-base Volume groups job + List vfModules = request.getVfModules().values().stream().flatMap(vfKey -> vfKey.values().stream()).collect(Collectors.toList()); + List vgNonBaseJobs = new ArrayList<>(); + for( VfModule vfModule : vfModules){ + try { + if(!commandUtils.isVfModuleBaseModule(commandParentData.getModelInfo(CommandDataKey.SERVICE_MODEL_INFO).getModelVersionId(), vfModule.getModelInfo().getModelVersionId())) { + vgNonBaseJobs.add(jobsBrokerService.add( + jobAdapter.createChildJob(JobType.VolumeGroupInstantiation, Job.JobStatus.CREATING, vfModule, getSharedData(), dataForChild)).toString()); + } + } catch (AsdcCatalogException e) { + LOG.error("Failed to retrieve service definitions from SDC, for VfModule is BaseModule. Error: "+e.getMessage() , e); + return new NextCommand(Job.JobStatus.COMPLETED_WITH_ERRORS); + } + } + return new NextCommand(Job.JobStatus.RESOURCE_IN_PROGRESS, new WatchingCommand(getSharedData(), vgNonBaseJobs, false)); + } + + @Override + public WatchingCommandBaseModule init(JobSharedData sharedData, Map commandData) { + super.init(sharedData, commandData); + commandParentData.initParentData(commandData); + return this; + } + + protected Map buildDataForChild() { + return commandParentData.getParentData(); + } + + @Override + public Map getData() { + return ImmutableMap.builder() + .putAll(super.getData()) + .putAll(commandParentData.getParentData()) + .build(); + } + + +} -- cgit 1.2.3-korg