diff options
Diffstat (limited to 'vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt')
-rw-r--r-- | vid-app-common/src/main/java/org/onap/vid/job/command/ResourceCommand.kt | 324 |
1 files changed, 324 insertions, 0 deletions
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<RequestDetailsWrapper<out Any>>, + val userId: Optional<String>, + 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<InternalState, () -> 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<String> = 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<String, Any?> { + 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<String, Any>): 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<String>())) + 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<String, Any>, 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 + } +} + + + |