CCSDK-3531 improve cmd-exec returned err msg
[ccsdk/cds.git] / ms / blueprintsprocessor / modules / services / execution-service / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / services / execution / RemoteScriptExecutionService.kt
1 /*
2  *  Copyright © 2019 IBM.
3  *  Modifications Copyright © 2020 Bell Canada.
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17
18 package org.onap.ccsdk.cds.blueprintsprocessor.services.execution
19
20 import com.fasterxml.jackson.databind.JsonNode
21 import com.google.protobuf.Struct
22 import com.google.protobuf.Timestamp
23 import com.google.protobuf.util.JsonFormat
24 import io.grpc.ManagedChannel
25 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.PrepareRemoteEnvInput
26 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.RemoteIdentifier
27 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.RemoteScriptUploadBlueprintInput
28 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.RemoteScriptExecutionInput
29 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.RemoteScriptExecutionOutput
30 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.RemoteScriptUploadBlueprintOutput
31 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.StatusType
32 import org.onap.ccsdk.cds.blueprintsprocessor.grpc.service.BluePrintGrpcClientService
33 import org.onap.ccsdk.cds.blueprintsprocessor.grpc.service.BluePrintGrpcLibPropertyService
34 import org.onap.ccsdk.cds.controllerblueprints.command.api.CommandExecutorServiceGrpc
35 import org.onap.ccsdk.cds.controllerblueprints.command.api.ExecutionInput
36 import org.onap.ccsdk.cds.controllerblueprints.command.api.ExecutionOutput
37 import org.onap.ccsdk.cds.controllerblueprints.command.api.Identifiers
38 import org.onap.ccsdk.cds.controllerblueprints.command.api.Packages
39 import org.onap.ccsdk.cds.controllerblueprints.command.api.PrepareEnvInput
40 import org.onap.ccsdk.cds.controllerblueprints.command.api.UploadBlueprintInput
41 import org.onap.ccsdk.cds.controllerblueprints.command.api.UploadBlueprintOutput
42 import org.onap.ccsdk.cds.controllerblueprints.core.jsonAsJsonType
43 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
44 import org.slf4j.LoggerFactory
45 import org.springframework.beans.factory.config.ConfigurableBeanFactory
46 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
47 import org.springframework.context.annotation.Scope
48 import org.springframework.stereotype.Service
49 import java.util.concurrent.TimeUnit
50
51 interface RemoteScriptExecutionService {
52
53     suspend fun init(selector: Any)
54     suspend fun uploadBlueprint(uploadBpInput: RemoteScriptUploadBlueprintInput): RemoteScriptUploadBlueprintOutput
55     suspend fun prepareEnv(prepareEnvInput: PrepareRemoteEnvInput): RemoteScriptExecutionOutput
56     suspend fun executeCommand(remoteExecutionInput: RemoteScriptExecutionInput): RemoteScriptExecutionOutput
57     suspend fun close()
58 }
59
60 @Service(ExecutionServiceConstant.SERVICE_GRPC_REMOTE_SCRIPT_EXECUTION)
61 @ConditionalOnProperty(
62     prefix = "blueprintprocessor.remoteScriptCommand", name = arrayOf("enabled"),
63     havingValue = "true", matchIfMissing = false
64 )
65 @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
66 class GrpcRemoteScriptExecutionService(private val bluePrintGrpcLibPropertyService: BluePrintGrpcLibPropertyService) :
67     RemoteScriptExecutionService {
68
69     private val log = LoggerFactory.getLogger(GrpcRemoteScriptExecutionService::class.java)!!
70
71     private var channel: ManagedChannel? = null
72     private lateinit var commandExecutorServiceGrpc: CommandExecutorServiceGrpc.CommandExecutorServiceBlockingStub
73
74     override suspend fun init(selector: Any) {
75         // Get the GRPC Client Service based on selector
76         val grpcClientService: BluePrintGrpcClientService = if (selector is JsonNode) {
77             bluePrintGrpcLibPropertyService.blueprintGrpcClientService(selector)
78         } else {
79             bluePrintGrpcLibPropertyService.blueprintGrpcClientService(selector.toString())
80         }
81
82         // Get the GRPC Channel
83         channel = grpcClientService.channel()
84         // Create Non Blocking Stub
85         commandExecutorServiceGrpc = CommandExecutorServiceGrpc.newBlockingStub(channel)
86
87         checkNotNull(commandExecutorServiceGrpc) {
88             "failed to create command executor grpc client for selector($selector)"
89         }
90     }
91
92     override suspend fun uploadBlueprint(uploadBPInput: RemoteScriptUploadBlueprintInput): RemoteScriptUploadBlueprintOutput {
93         val logPart = "requestId(${uploadBPInput.requestId}) subRequestId(${uploadBPInput.subRequestId}) blueprintName(${uploadBPInput.remoteIdentifier?.blueprintName}) blueprintVersion(${uploadBPInput.remoteIdentifier?.blueprintVersion}) blueprintUUID(${uploadBPInput.remoteIdentifier?.blueprintUUID})"
94         val grpcResponse = commandExecutorServiceGrpc
95             .withDeadlineAfter(uploadBPInput.timeOut * 1000, TimeUnit.MILLISECONDS)
96             .uploadBlueprint(uploadBPInput.asGrpcData())
97         checkNotNull(grpcResponse.status) {
98             "failed to get GRPC upload CBA response status for $logPart"
99         }
100         val uploadBlueprinOutput = grpcResponse.asJavaData()
101         log.info("Received Upload CBA response status(${uploadBlueprinOutput.status}) for $logPart payload(${uploadBlueprinOutput.payload})")
102         return uploadBlueprinOutput
103     }
104
105     override suspend fun prepareEnv(prepareEnvInput: PrepareRemoteEnvInput): RemoteScriptExecutionOutput {
106         val grpResponse = commandExecutorServiceGrpc
107             .withDeadlineAfter(prepareEnvInput.timeOut * 1000, TimeUnit.MILLISECONDS)
108             .prepareEnv(prepareEnvInput.asGrpcData())
109
110         checkNotNull(grpResponse.status) {
111             "failed to get GRPC prepare env response status for requestId(${prepareEnvInput.requestId})"
112         }
113
114         val remoteScriptExecutionOutput = grpResponse.asJavaData()
115         log.debug("Received prepare env response from command server for requestId(${prepareEnvInput.requestId})")
116
117         return remoteScriptExecutionOutput
118     }
119
120     override suspend fun executeCommand(remoteExecutionInput: RemoteScriptExecutionInput): RemoteScriptExecutionOutput {
121         val grpResponse =
122             commandExecutorServiceGrpc
123                 .withDeadlineAfter(remoteExecutionInput.timeOut * 1000, TimeUnit.MILLISECONDS)
124                 .executeCommand(remoteExecutionInput.asGrpcData())
125
126         checkNotNull(grpResponse.status) {
127             "failed to get GRPC response status for requestId(${remoteExecutionInput.requestId})"
128         }
129
130         log.debug("Received response from command server for requestId(${remoteExecutionInput.requestId})")
131         return grpResponse.asJavaData()
132     }
133
134     override suspend fun close() {
135         channel?.shutdownNow()
136     }
137
138     fun RemoteScriptUploadBlueprintInput.asGrpcData(): UploadBlueprintInput {
139         val correlationId = this.correlationId ?: this.requestId
140         return UploadBlueprintInput.newBuilder()
141             .setIdentifiers(this.remoteIdentifier!!.asGrpcData())
142             .setRequestId(this.requestId)
143             .setSubRequestId(this.subRequestId)
144             .setOriginatorId(this.originatorId)
145             .setCorrelationId(correlationId)
146             .setTimestamp(Timestamp.getDefaultInstance())
147             .setBinData(this.binData)
148             .setArchiveType(this.archiveType)
149             .setTimeOut(this.timeOut.toInt())
150             .build()
151     }
152
153     fun PrepareRemoteEnvInput.asGrpcData(): PrepareEnvInput {
154         val correlationId = this.correlationId ?: this.requestId
155
156         val packageList = mutableListOf<Packages>()
157
158         this.packages.toList().forEach {
159             val pckage = Packages.newBuilder()
160             JsonFormat.parser().merge(it.toString(), pckage)
161             packageList.add(pckage.build())
162         }
163
164         return PrepareEnvInput.newBuilder()
165             .setIdentifiers(this.remoteIdentifier!!.asGrpcData())
166             .setRequestId(this.requestId)
167             .setSubRequestId(this.subRequestId)
168             .setOriginatorId(this.originatorId)
169             .setCorrelationId(correlationId)
170             .setTimeOut(this.timeOut.toInt())
171             .addAllPackages(packageList)
172             .setProperties(this.properties.asGrpcData())
173             .build()
174     }
175
176     fun RemoteScriptExecutionInput.asGrpcData(): ExecutionInput {
177         val correlationId = this.correlationId ?: this.requestId
178         return ExecutionInput.newBuilder()
179             .setRequestId(this.requestId)
180             .setSubRequestId(this.subRequestId)
181             .setOriginatorId(this.originatorId)
182             .setCorrelationId(correlationId)
183             .setIdentifiers(this.remoteIdentifier!!.asGrpcData())
184             .setCommand(this.command)
185             .setTimeOut(this.timeOut.toInt())
186             .setProperties(this.properties.asGrpcData())
187             .setTimestamp(Timestamp.getDefaultInstance())
188             .build()
189     }
190
191     fun RemoteIdentifier.asGrpcData(): Identifiers? {
192         return Identifiers.newBuilder()
193             .setBlueprintName(this.blueprintName)
194             .setBlueprintVersion(this.blueprintVersion)
195             .setBlueprintUUID(this.blueprintUUID)
196             .build()
197     }
198
199     fun Map<String, JsonNode>.asGrpcData(): Struct {
200         val struct = Struct.newBuilder()
201         JsonFormat.parser().merge(JacksonUtils.getJson(this), struct)
202         return struct.build()
203     }
204
205     fun ExecutionOutput.asJavaData(): RemoteScriptExecutionOutput {
206         return RemoteScriptExecutionOutput(
207             requestId = this.requestId,
208             response = this.responseList,
209             status = StatusType.valueOf(this.status.name),
210             payload = payload.jsonAsJsonType(),
211             errMsg = errMsg
212         )
213     }
214
215     fun UploadBlueprintOutput.asJavaData(): RemoteScriptUploadBlueprintOutput {
216         return RemoteScriptUploadBlueprintOutput(
217             requestId = this.requestId,
218             subRequestId = this.subRequestId,
219             status = StatusType.valueOf(this.status.name),
220             payload = payload.jsonAsJsonType()
221         )
222     }
223 }