2 * Copyright © 2019 Orange
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.onap.ccsdk.cds.blueprintsprocessor.services.execution.scripts
19 import com.fasterxml.jackson.databind.node.ObjectNode
20 import com.fasterxml.jackson.databind.ObjectMapper
21 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
22 import org.onap.ccsdk.cds.blueprintsprocessor.rest.BasicAuthRestClientProperties
23 import org.onap.ccsdk.cds.blueprintsprocessor.rest.RestClientProperties
24 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BasicAuthRestClientService
25 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
26 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.RestLoggerService
27 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
28 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
29 import org.apache.commons.io.IOUtils
30 import org.apache.commons.io.FilenameUtils
31 import org.apache.http.client.entity.EntityBuilder
32 import org.apache.http.entity.ContentType
33 import org.apache.http.message.BasicHeader
34 import org.apache.http.client.methods.HttpPost
35 import org.apache.http.client.methods.HttpUriRequest
36 import org.apache.http.client.ClientProtocolException
37 import org.slf4j.LoggerFactory
38 import org.springframework.http.HttpMethod
39 import org.springframework.web.client.RestTemplate
40 import com.fasterxml.jackson.annotation.JsonIgnore
41 import com.fasterxml.jackson.annotation.JsonProperty
42 import java.util.ArrayList
43 import java.io.IOException
45 import java.nio.file.Files
46 import java.nio.file.Paths
47 import java.nio.file.Path
48 import org.springframework.http.HttpHeaders
49 import org.springframework.http.MediaType
50 import java.nio.charset.Charset
51 import java.util.Base64
52 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
54 import java.io.BufferedInputStream
55 import java.io.FileInputStream
56 import java.io.FileOutputStream
57 import java.util.zip.GZIPInputStream
59 open class K8sProfileUpload : AbstractScriptComponentFunction() {
61 private val log = LoggerFactory.getLogger(K8sProfileUpload::class.java)!!
63 override fun getName(): String {
64 return "K8sProfileUpload"
67 override suspend fun processNB(executionRequest: ExecutionServiceInput) {
68 log.info("executing K8s Profile Upload script")
70 val baseK8sApiUrl = getDynamicProperties("api-access").get("url").asText()
71 val k8sApiUsername = getDynamicProperties("api-access").get("username").asText()
72 val k8sApiPassword = getDynamicProperties("api-access").get("password").asText()
74 val prefixList: ArrayList<String> = getTemplatePrefixList(executionRequest)
76 for (prefix in prefixList) {
77 if (prefix.toLowerCase().equals("vnf")) {
78 log.info("For vnf-level resource-assignment, profile upload is not performed")
82 val assignmentParams = getDynamicProperties("assignment-params")
83 val payloadObject = JacksonUtils.jsonNode(assignmentParams.get(prefix).asText()) as ObjectNode
85 log.info("Uploading K8S profile for template prefix $prefix")
87 val vfModuleModelInvariantUuid: String = getResolvedParameter(payloadObject, "vf-module-model-invariant-uuid")
88 val vfModuleModelUuid: String = getResolvedParameter(payloadObject, "vf-module-model-version")
89 val k8sRbProfileName: String = getResolvedParameter(payloadObject, "k8s-rb-profile-name")
90 val k8sRbProfileNamespace: String = getResolvedParameter(payloadObject, "k8s-rb-profile-namespace")
92 val api = K8sApi(k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleModelInvariantUuid, vfModuleModelUuid)
94 if (!api.hasDefinition()) {
95 throw BluePrintProcessorException("K8s RB Definition (${vfModuleModelInvariantUuid}/${vfModuleModelUuid}) not found ")
98 log.info("k8s-rb-profile-name: $k8sRbProfileName")
99 if (k8sRbProfileName.equals("")) {
100 throw BluePrintProcessorException("K8s rb profile name is empty! Either define profile name to use or choose default")
102 if (k8sRbProfileName.equals("default") and api.hasProfile(k8sRbProfileName)) {
103 log.info("Using default profile - skipping upload")
105 if (api.hasProfile(k8sRbProfileName)) {
106 log.info("Profile Already Existing - skipping upload")
108 val profileFilePath = prepareProfileFile(k8sRbProfileName)
110 var profile = K8sProfile()
111 profile.profileName = k8sRbProfileName
112 profile.rbName = vfModuleModelInvariantUuid
113 profile.rbVersion = vfModuleModelUuid
114 profile.namespace = k8sRbProfileNamespace
115 api.createProfile(profile)
116 api.uploadProfileContent(profile, profileFilePath)
118 log.info("K8s Profile Upload Completed")
124 fun prepareProfileFile(k8sRbProfileName: String): Path {
125 val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
126 val bluePrintBasePath: String = bluePrintContext.rootPath
127 var profileFilePath: Path = Paths.get(bluePrintBasePath.plus(File.separator).plus("Templates").plus(File.separator).plus("k8s-profiles").plus(File.separator).plus("${k8sRbProfileName}.tar.gz"))
128 log.info("Reading K8s profile file: ${profileFilePath}")
130 val profileFile = profileFilePath.toFile()
132 if (!profileFile.exists())
133 throw BluePrintProcessorException("K8s Profile template file ${profileFilePath} does not exists")
135 return profileFilePath
138 fun getTemplatePrefixList(executionRequest: ExecutionServiceInput): ArrayList<String> {
139 val result = ArrayList<String>()
140 for (prefix in executionRequest.payload.get("resource-assignment-request").get("template-prefix").elements())
141 result.add(prefix.asText())
145 fun getResolvedParameter(payload: ObjectNode, keyName: String): String {
146 for (node in payload.get("resource-accumulator-resolved-data").elements()) {
147 if (node.get("param-name").asText().equals(keyName)) {
148 return node.get("param-value").asText()
154 override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
155 log.info("Executing Recovery")
156 bluePrintRuntimeService.getBluePrintError().addError("${runtimeException.message}")
159 inner class K8sApi(val username: String, val password: String, val baseUrl: String, val definition: String,
160 val definitionVersion: String) {
161 private val service: UploadFileRestClientService //BasicAuthRestClientService
164 var mapOfHeaders = hashMapOf<String, String>()
165 mapOfHeaders.put("Accept", "application/json")
166 mapOfHeaders.put("Content-Type", "application/json")
167 mapOfHeaders.put("cache-control", " no-cache")
168 mapOfHeaders.put("Accept", "application/json")
169 var basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
170 basicAuthRestClientProperties.username = username
171 basicAuthRestClientProperties.password = password
172 basicAuthRestClientProperties.url = "$baseUrl/v1/rb/definition/${definition}/${definitionVersion}"
173 basicAuthRestClientProperties.additionalHeaders = mapOfHeaders
175 this.service = UploadFileRestClientService(basicAuthRestClientProperties)
178 fun hasDefinition(): Boolean {
180 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name, "", "")
182 if (result.status >= 200 && result.status < 300)
186 } catch (e: Exception) {
187 log.info("Caught exception trying to get k8s rb definition")
188 throw BluePrintProcessorException("${e.message}")
192 fun hasProfile(profileName: String): Boolean {
194 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name,
195 "/profile/$profileName", "")
196 if (result.status >= 200 && result.status < 300)
202 } catch (e: Exception) {
203 log.info("Caught exception trying to get k8s rb profile")
204 throw BluePrintProcessorException("${e.message}")
208 fun createProfile(profile: K8sProfile) {
209 val objectMapper = ObjectMapper()
210 val profileJsonString: String = objectMapper.writeValueAsString(profile)
212 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.POST.name,
213 "/profile", profileJsonString)
214 if (result.status < 200 || result.status >= 300) {
215 throw Exception(result.body)
217 } catch (e: Exception) {
218 log.info("Caught exception trying to create k8s rb profile ${profile.profileName}")
219 throw BluePrintProcessorException("${e.message}")
223 fun uploadProfileContent(profile: K8sProfile, filePath: Path) {
225 val result: BlueprintWebClientService.WebClientResponse<String> = service.uploadBinaryFile(
226 "/profile/${profile.profileName}/content", filePath)
227 if (result.status < 200 || result.status >= 300) {
228 throw Exception(result.body)
230 } catch (e: Exception) {
231 log.info("Caught exception trying to upload k8s rb profile ${profile.profileName}")
232 throw BluePrintProcessorException("${e.message}")
238 class UploadFileRestClientService(
239 private val restClientProperties:
240 BasicAuthRestClientProperties
241 ) : BlueprintWebClientService {
243 override fun defaultHeaders(): Map<String, String> {
245 val encodedCredentials = setBasicAuth(
246 restClientProperties.username,
247 restClientProperties.password
250 HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
251 HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,
252 HttpHeaders.AUTHORIZATION to "Basic $encodedCredentials"
256 override fun host(uri: String): String {
257 return restClientProperties.url + uri
260 override fun convertToBasicHeaders(headers: Map<String, String>):
262 val customHeaders: MutableMap<String, String> = headers.toMutableMap()
263 // inject additionalHeaders
264 customHeaders.putAll(verifyAdditionalHeaders(restClientProperties))
266 if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
267 val encodedCredentials = setBasicAuth(
268 restClientProperties.username,
269 restClientProperties.password
271 customHeaders[HttpHeaders.AUTHORIZATION] =
272 "Basic $encodedCredentials"
274 return super.convertToBasicHeaders(customHeaders)
277 private fun setBasicAuth(username: String, password: String): String {
278 val credentialsString = "$username:$password"
279 return Base64.getEncoder().encodeToString(
280 credentialsString.toByteArray(Charset.defaultCharset())
284 @Throws(IOException::class, ClientProtocolException::class)
285 private fun performHttpCall(httpUriRequest: HttpUriRequest): BlueprintWebClientService.WebClientResponse<String> {
286 val httpResponse = httpClient().execute(httpUriRequest)
287 val statusCode = httpResponse.statusLine.statusCode
288 httpResponse.entity.content.use {
289 val body = IOUtils.toString(it, Charset.defaultCharset())
290 return BlueprintWebClientService.WebClientResponse(statusCode, body)
294 fun uploadBinaryFile(path: String, filePath: Path): BlueprintWebClientService.WebClientResponse<String> {
295 val convertedHeaders: Array<BasicHeader> = convertToBasicHeaders(defaultHeaders())
296 val httpPost = HttpPost(host(path))
297 val entity = EntityBuilder.create().setBinary(Files.readAllBytes(filePath)).build()
298 httpPost.setEntity(entity)
299 RestLoggerService.httpInvoking(convertedHeaders)
300 httpPost.setHeaders(convertedHeaders)
301 return performHttpCall(httpPost)
306 @get:JsonProperty("rb-name")
307 var rbName: String? = null
308 @get:JsonProperty("rb-version")
309 var rbVersion: String? = null
310 @get:JsonProperty("profile-name")
311 var profileName: String? = null
312 @get:JsonProperty("namespace")
313 var namespace: String? = "default"
315 override fun toString(): String {
316 return "$rbName:$rbVersion:$profileName"
319 override fun equals(other: Any?): Boolean {
320 if (this === other) return true
321 if (javaClass != other?.javaClass) return false
325 override fun hashCode(): Int {
326 return javaClass.hashCode()