1 package org.onap.vid.job.command
3 import com.fasterxml.jackson.module.kotlin.convertValue
4 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate
5 import org.onap.vid.changeManagement.RequestDetailsWrapper
6 import org.onap.vid.job.Job
7 import org.onap.vid.job.Job.JobStatus
8 import org.onap.vid.job.JobAdapter
9 import org.onap.vid.job.JobCommand
10 import org.onap.vid.job.NextCommand
11 import org.onap.vid.job.impl.JobSharedData
12 import org.onap.vid.model.Action
13 import org.onap.vid.model.RequestReferencesContainer
14 import org.onap.vid.model.serviceInstantiation.BaseResource
15 import org.onap.vid.mso.RestMsoImplementation
16 import org.onap.vid.utils.JACKSON_OBJECT_MAPPER
17 import org.onap.vid.utils.getEnumFromMapOfStrings
18 import org.springframework.http.HttpMethod
21 const val INTERNAL_STATE = "internalState"
22 const val ACTION_PHASE = "actionPhase"
23 const val CHILD_JOBS = "childJobs"
24 const val MSO_RESOURCE_ID = "msoResourceIds"
25 const val CUMULATIVE_STATUS = "cumulativeStatus"
27 enum class InternalState constructor(val immediate:Boolean=false) {
29 CREATING_CHILDREN(true),
37 data class NextInternalState(val nextActionPhase: Action, val nextInternalState: InternalState)
40 data class MsoRestCallPlan(
41 val httpMethod: HttpMethod,
43 val payload: Optional<RequestDetailsWrapper<out Any>>,
44 val userId: Optional<String>,
45 val actionDescription: String
48 abstract class ResourceCommand(
49 protected val restMso: RestMsoImplementation,
50 protected val inProgressStatusService: InProgressStatusService,
51 protected val msoResultHandlerService: MsoResultHandlerService,
52 protected val watchChildrenJobsBL: WatchChildrenJobsBL
53 ) : CommandBase(), JobCommand {
56 private val Logger = EELFLoggerDelegate.getLogger(ResourceCommand::class.java)
59 abstract fun createChildren():JobStatus
61 abstract fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan
63 abstract fun planDeleteMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan
65 private val commandByInternalState: Map<InternalState, () -> JobStatus> = hashMapOf(
66 Pair(InternalState.CREATING_CHILDREN, ::createChildren),
67 Pair(InternalState.WATCHING, ::watchChildren),
68 Pair(InternalState.CREATE_MYSELF, ::createMyself),
69 Pair(InternalState.DELETE_MYSELF, ::deleteMyself),
70 Pair(InternalState.IN_PROGRESS, ::inProgress)
73 private lateinit var internalState:InternalState
74 protected lateinit var actionPhase: Action
75 private var commandParentData: CommandParentData = CommandParentData()
76 private var msoResourceIds: MsoResourceIds = EMPTY_MSO_RESOURCE_ID
77 protected var childJobs:List<String> = emptyList()
78 private lateinit var cumulativeStatus:JobStatus
81 override fun call(): NextCommand {
82 var jobStatus:JobStatus = invokeCommand()
83 jobStatus = comulateStatusAndUpdatePropertyIfFinal(jobStatus)
85 Logger.debug("command for job ${sharedData.jobUuid} invoked and finished with jobStatus $jobStatus")
86 if (shallStopJob(jobStatus)) {
88 return NextCommand(jobStatus)
91 val (nextActionPhase, nextInternalState) = calcNextInternalState(jobStatus, internalState, actionPhase)
92 Logger.debug("next state for job ${sharedData.jobUuid} is $nextInternalState")
93 actionPhase = nextActionPhase
94 internalState = nextInternalState
96 if (internalState==InternalState.TERMINAL) {
98 return NextCommand(jobStatus)
101 jobStatus = getExternalInProgressStatus()
102 Logger.debug("next status for job ${sharedData.jobUuid} is $jobStatus")
103 // if (internalState.immediate) return call() //shortcut instead of execute another command
104 return NextCommand(jobStatus, this)
107 //we want to stop in faliures, except for service witn no action, since service with no action trigger 2 phases (delete and create)
108 protected fun shallStopJob(jobStatus: JobStatus) =
109 jobStatus.isFailure && !(isServiceCommand() && getActionType()==Action.None)
111 //this method is used to expose the job status after successful completion of current state
112 //should be override by subclass (like ServiceCommand) that need to return other default job status
113 protected open fun getExternalInProgressStatus() = JobStatus.RESOURCE_IN_PROGRESS
115 private fun invokeCommand(): JobStatus {
116 return commandByInternalState.getOrDefault (internalState, ::throwIllegalState).invoke()
119 private fun throwIllegalState():JobStatus {
120 throw IllegalStateException("can't find action for pashe $actionPhase and state $internalState")
123 private fun calcNextInternalState(jobStatus: JobStatus, internalState: InternalState, actionPhase: Action): NextInternalState {
125 val nextInternalState = when (actionPhase) {
126 Action.Delete -> calcNextStateDeletePhase(jobStatus, internalState)
127 Action.Create -> calcNextStateCreatePhase(jobStatus, internalState)
128 else -> InternalState.TERMINAL
131 if (nextInternalState == InternalState.TERMINAL
132 && actionPhase == Action.Delete
133 && isServiceCommand()) {
134 // Loop over to "Create" phase
135 return NextInternalState(Action.Create, InternalState.INITIAL)
138 return NextInternalState(actionPhase, nextInternalState)
142 //no need to refer to failed (final) states here
143 //This method is called only for non final states or COMPLETED
144 protected fun calcNextStateDeletePhase(jobStatus: JobStatus, internalState: InternalState): InternalState {
145 return when (internalState) {
147 InternalState.CREATING_CHILDREN -> InternalState.WATCHING
149 InternalState.WATCHING -> {
151 !jobStatus.isFinal -> InternalState.WATCHING
152 isNeedToDeleteMyself() -> InternalState.DELETE_MYSELF
153 else -> InternalState.TERMINAL
157 InternalState.DELETE_MYSELF -> InternalState.IN_PROGRESS
159 InternalState.IN_PROGRESS -> {
160 if (jobStatus == Job.JobStatus.COMPLETED) InternalState.TERMINAL else InternalState.IN_PROGRESS
163 else -> InternalState.TERMINAL
167 protected fun calcNextStateCreatePhase(jobStatus: JobStatus, internalState: InternalState): InternalState {
168 return when (internalState) {
170 InternalState.CREATE_MYSELF -> InternalState.IN_PROGRESS
172 InternalState.IN_PROGRESS -> {
173 if (jobStatus == Job.JobStatus.COMPLETED) InternalState.CREATING_CHILDREN else InternalState.IN_PROGRESS
176 InternalState.CREATING_CHILDREN -> InternalState.WATCHING
178 InternalState.WATCHING -> {
180 !jobStatus.isFinal -> InternalState.WATCHING
181 else -> InternalState.TERMINAL
186 else -> InternalState.TERMINAL
190 override fun getData(): Map<String, Any?> {
192 ACTION_PHASE to actionPhase,
193 INTERNAL_STATE to internalState,
194 MSO_RESOURCE_ID to msoResourceIds,
195 CHILD_JOBS to childJobs,
196 CUMULATIVE_STATUS to cumulativeStatus
200 override fun init(sharedData: JobSharedData, commandData: Map<String, Any>): ResourceCommand {
202 val resourceIdsRaw:Any? = commandData[MSO_RESOURCE_ID]
203 commandParentData.initParentData(commandData)
205 if (resourceIdsRaw != null) JACKSON_OBJECT_MAPPER.convertValue(resourceIdsRaw)
206 else EMPTY_MSO_RESOURCE_ID
208 childJobs = JACKSON_OBJECT_MAPPER.convertValue(commandData.getOrDefault(CHILD_JOBS, emptyList<String>()))
209 cumulativeStatus = getEnumFromMapOfStrings(commandData, CUMULATIVE_STATUS, JobStatus.COMPLETED_WITH_NO_ACTION)
210 actionPhase = getEnumFromMapOfStrings(commandData, ACTION_PHASE, Action.Delete)
211 internalState = calcInitialState(commandData, actionPhase)
215 private fun calcInitialState(commandData: Map<String, Any>, phase: Action):InternalState {
216 val status:InternalState = getEnumFromMapOfStrings(commandData, INTERNAL_STATE, InternalState.INITIAL)
217 if (status == InternalState.INITIAL) {
219 return when (phase) {
220 Action.Delete -> InternalState.CREATING_CHILDREN
221 Action.Create -> if (isNeedToCreateMyself()) InternalState.CREATE_MYSELF else InternalState.CREATING_CHILDREN
222 else -> throw IllegalStateException("state $internalState is not supported yet")
228 //command may override it in order to do something while init state
229 protected open fun onInitial(phase: Action) {
233 //command may override it in order to do something while final status
234 protected open fun onFinal(jobStatus: JobStatus) {
238 protected open fun getRequest(): BaseResource {
239 return sharedData.request as BaseResource
242 protected open fun getActionType(): Action {
243 return getRequest().action
246 protected open fun isServiceCommand(): Boolean = false
248 protected open fun isNeedToDeleteMyself(): Boolean = getActionType() == Action.Delete
250 protected open fun isNeedToCreateMyself(): Boolean = getActionType() == Action.Create
252 protected open fun inProgress(): Job.JobStatus {
253 val requestId:String = msoResourceIds.requestId;
255 val jobStatus = inProgressStatusService.call(getExpiryChecker(), sharedData, requestId)
256 handleInProgressStatus(jobStatus)
257 } catch (e: javax.ws.rs.ProcessingException) {
258 // Retry when we can't connect MSO during getStatus
259 Logger.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, will retry: {}", requestId, e, e)
260 Job.JobStatus.IN_PROGRESS;
261 } catch (e: InProgressStatusService.BadResponseFromMso) {
262 inProgressStatusService.handleFailedMsoResponse(sharedData.jobUuid, requestId, e.msoResponse)
263 Job.JobStatus.IN_PROGRESS
264 } catch (e: RuntimeException) {
265 Logger.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, stopping: {}", requestId, e, e)
266 Job.JobStatus.STOPPED
270 fun createMyself(): Job.JobStatus {
271 val createMyselfCommand = planCreateMyselfRestCall(commandParentData, sharedData.request, sharedData.userId)
273 return executeAndHandleMsoInstanceRequest(createMyselfCommand)
276 fun deleteMyself(): Job.JobStatus {
277 val deleteMyselfCommand = planDeleteMyselfRestCall(commandParentData, sharedData.request, sharedData.userId)
279 return executeAndHandleMsoInstanceRequest(deleteMyselfCommand)
282 private fun executeAndHandleMsoInstanceRequest(restCallPlan: MsoRestCallPlan): JobStatus {
283 val msoResponse = restMso.restCall(
284 restCallPlan.httpMethod,
285 RequestReferencesContainer::class.java,
286 restCallPlan.payload.orElse(null),
291 val msoResult = if (isServiceCommand()) {
292 msoResultHandlerService.handleRootResponse(sharedData.jobUuid, msoResponse)
294 msoResultHandlerService.handleResponse(msoResponse, restCallPlan.actionDescription)
297 this.msoResourceIds = msoResult.msoResourceIds
298 return msoResult.jobStatus
301 protected open fun getExpiryChecker(): ExpiryChecker = ExpiryChecker {false}
303 protected open fun handleInProgressStatus(jobStatus: JobStatus): JobStatus {
304 return if (jobStatus == Job.JobStatus.PAUSE) Job.JobStatus.IN_PROGRESS else jobStatus
307 protected open fun watchChildren():JobStatus {
308 return watchChildrenJobsBL.retrieveChildrenJobsStatus(childJobs)
311 private fun comulateStatusAndUpdatePropertyIfFinal(internalStateStatus: JobStatus): JobStatus {
312 val status = watchChildrenJobsBL.cumulateJobStatus(internalStateStatus, cumulativeStatus)
314 //we want to update cumulativeStatus only for final status
315 if (status.isFinal) {
316 cumulativeStatus = status;