2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.vid.job.command
23 import com.fasterxml.jackson.module.kotlin.convertValue
24 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate
25 import org.onap.vid.changeManagement.RequestDetailsWrapper
26 import org.onap.vid.job.Job
27 import org.onap.vid.job.Job.JobStatus
28 import org.onap.vid.job.JobAdapter
29 import org.onap.vid.job.JobCommand
30 import org.onap.vid.job.NextCommand
31 import org.onap.vid.job.impl.JobSharedData
32 import org.onap.vid.model.Action
33 import org.onap.vid.model.RequestReferencesContainer
34 import org.onap.vid.model.serviceInstantiation.BaseResource
35 import org.onap.vid.mso.RestMsoImplementation
36 import org.onap.vid.utils.JACKSON_OBJECT_MAPPER
37 import org.onap.vid.utils.getEnumFromMapOfStrings
38 import org.springframework.http.HttpMethod
41 const val INTERNAL_STATE = "internalState"
42 const val ACTION_PHASE = "actionPhase"
43 const val CHILD_JOBS = "childJobs"
44 const val MSO_RESOURCE_ID = "msoResourceIds"
45 const val CUMULATIVE_STATUS = "cumulativeStatus"
47 enum class InternalState constructor(val immediate:Boolean=false) {
49 CREATING_CHILDREN(true),
57 data class NextInternalState(val nextActionPhase: Action, val nextInternalState: InternalState)
60 data class MsoRestCallPlan(
61 val httpMethod: HttpMethod,
63 val payload: Optional<RequestDetailsWrapper<out Any>>,
64 val userId: Optional<String>,
65 val actionDescription: String
68 abstract class ResourceCommand(
69 protected val restMso: RestMsoImplementation,
70 protected val inProgressStatusService: InProgressStatusService,
71 protected val msoResultHandlerService: MsoResultHandlerService,
72 protected val watchChildrenJobsBL: WatchChildrenJobsBL
73 ) : CommandBase(), JobCommand {
76 private val Logger = EELFLoggerDelegate.getLogger(ResourceCommand::class.java)
79 abstract fun createChildren():JobStatus
81 abstract fun planCreateMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan
83 abstract fun planDeleteMyselfRestCall(commandParentData: CommandParentData, request: JobAdapter.AsyncJobRequest, userId: String): MsoRestCallPlan
85 private val commandByInternalState: Map<InternalState, () -> JobStatus> = hashMapOf(
86 Pair(InternalState.CREATING_CHILDREN, ::createChildren),
87 Pair(InternalState.WATCHING, ::watchChildren),
88 Pair(InternalState.CREATE_MYSELF, ::createMyself),
89 Pair(InternalState.DELETE_MYSELF, ::deleteMyself),
90 Pair(InternalState.IN_PROGRESS, ::inProgress)
93 private lateinit var internalState:InternalState
94 protected lateinit var actionPhase: Action
95 private var commandParentData: CommandParentData = CommandParentData()
96 private var msoResourceIds: MsoResourceIds = EMPTY_MSO_RESOURCE_ID
97 protected var childJobs:List<String> = emptyList()
98 private lateinit var cumulativeStatus:JobStatus
101 override fun call(): NextCommand {
102 var jobStatus:JobStatus = invokeCommand()
103 jobStatus = comulateStatusAndUpdatePropertyIfFinal(jobStatus)
105 Logger.debug("command for job ${sharedData.jobUuid} invoked and finished with jobStatus $jobStatus")
106 if (shallStopJob(jobStatus)) {
108 return NextCommand(jobStatus)
111 val (nextActionPhase, nextInternalState) = calcNextInternalState(jobStatus, internalState, actionPhase)
112 Logger.debug("next state for job ${sharedData.jobUuid} is $nextInternalState")
113 actionPhase = nextActionPhase
114 internalState = nextInternalState
116 if (internalState==InternalState.TERMINAL) {
118 return NextCommand(jobStatus)
121 jobStatus = getExternalInProgressStatus()
122 Logger.debug("next status for job ${sharedData.jobUuid} is $jobStatus")
123 // if (internalState.immediate) return call() //shortcut instead of execute another command
124 return NextCommand(jobStatus, this)
127 //we want to stop in faliures, except for service witn no action, since service with no action trigger 2 phases (delete and create)
128 protected fun shallStopJob(jobStatus: JobStatus) =
129 jobStatus.isFailure && !(isServiceCommand() && getActionType()==Action.None)
131 //this method is used to expose the job status after successful completion of current state
132 //should be override by subclass (like ServiceCommand) that need to return other default job status
133 protected open fun getExternalInProgressStatus() = JobStatus.RESOURCE_IN_PROGRESS
135 private fun invokeCommand(): JobStatus {
136 return commandByInternalState.getOrDefault (internalState, ::throwIllegalState).invoke()
139 private fun throwIllegalState():JobStatus {
140 throw IllegalStateException("can't find action for pashe $actionPhase and state $internalState")
143 private fun calcNextInternalState(jobStatus: JobStatus, internalState: InternalState, actionPhase: Action): NextInternalState {
145 val nextInternalState = when (actionPhase) {
146 Action.Delete -> calcNextStateDeletePhase(jobStatus, internalState)
147 Action.Create -> calcNextStateCreatePhase(jobStatus, internalState)
148 else -> InternalState.TERMINAL
151 if (nextInternalState == InternalState.TERMINAL
152 && actionPhase == Action.Delete
153 && isServiceCommand()) {
154 // Loop over to "Create" phase
155 return NextInternalState(Action.Create, InternalState.INITIAL)
158 return NextInternalState(actionPhase, nextInternalState)
162 //no need to refer to failed (final) states here
163 //This method is called only for non final states or COMPLETED
164 protected fun calcNextStateDeletePhase(jobStatus: JobStatus, internalState: InternalState): InternalState {
165 return when (internalState) {
167 InternalState.CREATING_CHILDREN -> InternalState.WATCHING
169 InternalState.WATCHING -> {
171 !jobStatus.isFinal -> InternalState.WATCHING
172 isNeedToDeleteMyself() -> InternalState.DELETE_MYSELF
173 else -> InternalState.TERMINAL
177 InternalState.DELETE_MYSELF -> InternalState.IN_PROGRESS
179 InternalState.IN_PROGRESS -> {
180 if (jobStatus == Job.JobStatus.COMPLETED) InternalState.TERMINAL else InternalState.IN_PROGRESS
183 else -> InternalState.TERMINAL
187 protected fun calcNextStateCreatePhase(jobStatus: JobStatus, internalState: InternalState): InternalState {
188 return when (internalState) {
190 InternalState.CREATE_MYSELF -> InternalState.IN_PROGRESS
192 InternalState.IN_PROGRESS -> {
193 if (jobStatus == Job.JobStatus.COMPLETED) InternalState.CREATING_CHILDREN else InternalState.IN_PROGRESS
196 InternalState.CREATING_CHILDREN -> InternalState.WATCHING
198 InternalState.WATCHING -> {
200 !jobStatus.isFinal -> InternalState.WATCHING
201 else -> InternalState.TERMINAL
206 else -> InternalState.TERMINAL
210 override fun getData(): Map<String, Any?> {
212 ACTION_PHASE to actionPhase,
213 INTERNAL_STATE to internalState,
214 MSO_RESOURCE_ID to msoResourceIds,
215 CHILD_JOBS to childJobs,
216 CUMULATIVE_STATUS to cumulativeStatus
220 override fun init(sharedData: JobSharedData, commandData: Map<String, Any>): ResourceCommand {
222 val resourceIdsRaw:Any? = commandData[MSO_RESOURCE_ID]
223 commandParentData.initParentData(commandData)
225 if (resourceIdsRaw != null) JACKSON_OBJECT_MAPPER.convertValue(resourceIdsRaw)
226 else EMPTY_MSO_RESOURCE_ID
228 childJobs = JACKSON_OBJECT_MAPPER.convertValue(commandData.getOrDefault(CHILD_JOBS, emptyList<String>()))
229 cumulativeStatus = getEnumFromMapOfStrings(commandData, CUMULATIVE_STATUS, JobStatus.COMPLETED_WITH_NO_ACTION)
230 actionPhase = getEnumFromMapOfStrings(commandData, ACTION_PHASE, Action.Delete)
231 internalState = calcInitialState(commandData, actionPhase)
235 private fun calcInitialState(commandData: Map<String, Any>, phase: Action):InternalState {
236 val status:InternalState = getEnumFromMapOfStrings(commandData, INTERNAL_STATE, InternalState.INITIAL)
237 if (status == InternalState.INITIAL) {
239 return when (phase) {
240 Action.Delete -> InternalState.CREATING_CHILDREN
241 Action.Create -> if (isNeedToCreateMyself()) InternalState.CREATE_MYSELF else InternalState.CREATING_CHILDREN
242 else -> throw IllegalStateException("state $internalState is not supported yet")
248 //command may override it in order to do something while init state
249 protected open fun onInitial(phase: Action) {
253 //command may override it in order to do something while final status
254 protected open fun onFinal(jobStatus: JobStatus) {
258 protected open fun getRequest(): BaseResource {
259 return sharedData.request as BaseResource
262 protected open fun getActionType(): Action {
263 return getRequest().action
266 protected open fun isServiceCommand(): Boolean = false
268 protected open fun isNeedToDeleteMyself(): Boolean = getActionType() == Action.Delete
270 protected open fun isNeedToCreateMyself(): Boolean = getActionType() == Action.Create
272 protected open fun inProgress(): Job.JobStatus {
273 val requestId:String = msoResourceIds.requestId;
275 val jobStatus = inProgressStatusService.call(getExpiryChecker(), sharedData, requestId)
276 handleInProgressStatus(jobStatus)
277 } catch (e: javax.ws.rs.ProcessingException) {
278 // Retry when we can't connect MSO during getStatus
279 Logger.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, will retry: {}", requestId, e, e)
280 Job.JobStatus.IN_PROGRESS;
281 } catch (e: InProgressStatusService.BadResponseFromMso) {
282 inProgressStatusService.handleFailedMsoResponse(sharedData.jobUuid, requestId, e.msoResponse)
283 Job.JobStatus.IN_PROGRESS
284 } catch (e: RuntimeException) {
285 Logger.error(EELFLoggerDelegate.errorLogger, "Cannot get orchestration status for {}, stopping: {}", requestId, e, e)
286 Job.JobStatus.STOPPED
290 fun createMyself(): Job.JobStatus {
291 val createMyselfCommand = planCreateMyselfRestCall(commandParentData, sharedData.request, sharedData.userId)
293 return executeAndHandleMsoInstanceRequest(createMyselfCommand)
296 fun deleteMyself(): Job.JobStatus {
297 val deleteMyselfCommand = planDeleteMyselfRestCall(commandParentData, sharedData.request, sharedData.userId)
299 return executeAndHandleMsoInstanceRequest(deleteMyselfCommand)
302 private fun executeAndHandleMsoInstanceRequest(restCallPlan: MsoRestCallPlan): JobStatus {
303 val msoResponse = restMso.restCall(
304 restCallPlan.httpMethod,
305 RequestReferencesContainer::class.java,
306 restCallPlan.payload.orElse(null),
311 val msoResult = if (isServiceCommand()) {
312 msoResultHandlerService.handleRootResponse(sharedData.jobUuid, msoResponse)
314 msoResultHandlerService.handleResponse(msoResponse, restCallPlan.actionDescription)
317 this.msoResourceIds = msoResult.msoResourceIds
318 return msoResult.jobStatus
321 protected open fun getExpiryChecker(): ExpiryChecker = ExpiryChecker {false}
323 protected open fun handleInProgressStatus(jobStatus: JobStatus): JobStatus {
324 return if (jobStatus == Job.JobStatus.PAUSE) Job.JobStatus.IN_PROGRESS else jobStatus
327 protected open fun watchChildren():JobStatus {
328 return watchChildrenJobsBL.retrieveChildrenJobsStatus(childJobs)
331 private fun comulateStatusAndUpdatePropertyIfFinal(internalStateStatus: JobStatus): JobStatus {
332 val status = watchChildrenJobsBL.cumulateJobStatus(internalStateStatus, cumulativeStatus)
334 //we want to update cumulativeStatus only for final status
335 if (status.isFinal) {
336 cumulativeStatus = status;