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"))
80 val assignmentParams = getDynamicProperties("assignment-params")
81 val payloadObject = JacksonUtils.jsonNode(assignmentParams.get(prefix).asText()) as ObjectNode
83 log.info("Uploading K8S profile for template prefix $prefix")
85 val vfModuleModelInvariantUuid: String = getResolvedParameter(payloadObject, "vf-module-model-invariant-uuid")
86 val vfModuleModelUuid: String = getResolvedParameter(payloadObject, "vf-module-model-version")
87 val k8sRbProfileName: String = getResolvedParameter(payloadObject, "k8s-rb-profile-name")
88 val k8sRbProfileNamespace: String = getResolvedParameter(payloadObject, "k8s-rb-profile-namespace")
90 val api = K8sApi(k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleModelInvariantUuid, vfModuleModelUuid)
92 if (!api.hasDefinition()) {
93 throw BluePrintProcessorException("K8s RB Definition (${vfModuleModelInvariantUuid}/${vfModuleModelUuid}) not found ")
96 log.info("k8s-rb-profile-name: $k8sRbProfileName")
97 if (k8sRbProfileName.equals("")) {
98 log.info("Profile Name Not Defined - skipping upload")
100 if (api.hasProfile(k8sRbProfileName)) {
101 log.info("Profile Already Existing - skipping upload")
103 val profileFilePath = prepareProfileFile(k8sRbProfileName)
105 var profile = K8sProfile()
106 profile.profileName = k8sRbProfileName
107 profile.rbName = vfModuleModelInvariantUuid
108 profile.rbVersion = vfModuleModelUuid
109 profile.namespace = k8sRbProfileNamespace
110 api.createProfile(profile)
111 api.uploadProfileContent(profile, profileFilePath)
113 log.info("K8s Profile Upload Completed")
119 fun prepareProfileFile(k8sRbProfileName: String): Path {
120 val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
121 val bluePrintBasePath: String = bluePrintContext.rootPath
122 var profileFilePath: Path = Paths.get(bluePrintBasePath.plus(File.separator).plus("Templates").plus(File.separator).plus("k8s-profiles").plus(File.separator).plus("${k8sRbProfileName}.tar.gz"))
123 log.info("Reading K8s profile file: ${profileFilePath}")
125 val profileFile = profileFilePath.toFile()
127 if (!profileFile.exists())
128 throw BluePrintProcessorException("K8s Profile template file ${profileFilePath} does not exists")
130 return profileFilePath
133 fun getTemplatePrefixList(executionRequest: ExecutionServiceInput): ArrayList<String> {
134 val result = ArrayList<String>()
135 for (prefix in executionRequest.payload.get("resource-assignment-request").get("template-prefix").elements())
136 result.add(prefix.asText())
140 fun getResolvedParameter(payload: ObjectNode, keyName: String): String {
141 for (node in payload.get("resource-accumulator-resolved-data").elements()) {
142 if (node.get("param-name").asText().equals(keyName)) {
143 return node.get("param-value").asText()
149 override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
150 log.info("Executing Recovery")
151 bluePrintRuntimeService.getBluePrintError().addError("${runtimeException.message}")
154 inner class K8sApi(val username: String, val password: String, val baseUrl: String, val definition: String,
155 val definitionVersion: String) {
156 private val service: UploadFileRestClientService //BasicAuthRestClientService
159 var mapOfHeaders = hashMapOf<String, String>()
160 mapOfHeaders.put("Accept", "application/json")
161 mapOfHeaders.put("Content-Type", "application/json")
162 mapOfHeaders.put("cache-control", " no-cache")
163 mapOfHeaders.put("Accept", "application/json")
164 var basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
165 basicAuthRestClientProperties.username = username
166 basicAuthRestClientProperties.password = password
167 basicAuthRestClientProperties.url = "$baseUrl/api/multicloud-k8s/v1/v1/rb/definition/${definition}/${definitionVersion}"
168 basicAuthRestClientProperties.additionalHeaders = mapOfHeaders
170 this.service = UploadFileRestClientService(basicAuthRestClientProperties)
173 fun hasDefinition(): Boolean {
175 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name, "", "")
177 if (result.status >= 200 && result.status < 300)
181 } catch (e: Exception) {
182 log.info("Caught exception trying to get k8s rb definition")
183 throw BluePrintProcessorException("${e.message}")
187 fun hasProfile(profileName: String): Boolean {
189 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name,
190 "/profile/$profileName", "")
191 if (result.status >= 200 && result.status < 300)
197 } catch (e: Exception) {
198 log.info("Caught exception trying to get k8s rb profile")
199 throw BluePrintProcessorException("${e.message}")
203 fun createProfile(profile: K8sProfile) {
204 val objectMapper = ObjectMapper()
205 val profileJsonString: String = objectMapper.writeValueAsString(profile)
207 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.POST.name,
208 "/profile", profileJsonString)
209 if (result.status < 200 || result.status >= 300) {
210 throw Exception(result.body)
212 } catch (e: Exception) {
213 log.info("Caught exception trying to create k8s rb profile ${profile.profileName}")
214 throw BluePrintProcessorException("${e.message}")
218 fun uploadProfileContent(profile: K8sProfile, filePath: Path) {
220 val result: BlueprintWebClientService.WebClientResponse<String> = service.uploadBinaryFile(
221 "/profile/${profile.profileName}/content", filePath)
222 if (result.status < 200 || result.status >= 300) {
223 throw Exception(result.body)
225 } catch (e: Exception) {
226 log.info("Caught exception trying to upload k8s rb profile ${profile.profileName}")
227 throw BluePrintProcessorException("${e.message}")
233 class UploadFileRestClientService(
234 private val restClientProperties:
235 BasicAuthRestClientProperties
236 ) : BlueprintWebClientService {
238 override fun defaultHeaders(): Map<String, String> {
240 val encodedCredentials = setBasicAuth(
241 restClientProperties.username,
242 restClientProperties.password
245 HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
246 HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,
247 HttpHeaders.AUTHORIZATION to "Basic $encodedCredentials"
251 override fun host(uri: String): String {
252 return restClientProperties.url + uri
255 override fun convertToBasicHeaders(headers: Map<String, String>):
257 val customHeaders: MutableMap<String, String> = headers.toMutableMap()
258 // inject additionalHeaders
259 customHeaders.putAll(verifyAdditionalHeaders(restClientProperties))
261 if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
262 val encodedCredentials = setBasicAuth(
263 restClientProperties.username,
264 restClientProperties.password
266 customHeaders[HttpHeaders.AUTHORIZATION] =
267 "Basic $encodedCredentials"
269 return super.convertToBasicHeaders(customHeaders)
272 private fun setBasicAuth(username: String, password: String): String {
273 val credentialsString = "$username:$password"
274 return Base64.getEncoder().encodeToString(
275 credentialsString.toByteArray(Charset.defaultCharset())
279 @Throws(IOException::class, ClientProtocolException::class)
280 private fun performHttpCall(httpUriRequest: HttpUriRequest): BlueprintWebClientService.WebClientResponse<String> {
281 val httpResponse = httpClient().execute(httpUriRequest)
282 val statusCode = httpResponse.statusLine.statusCode
283 httpResponse.entity.content.use {
284 val body = IOUtils.toString(it, Charset.defaultCharset())
285 return BlueprintWebClientService.WebClientResponse(statusCode, body)
289 fun uploadBinaryFile(path: String, filePath: Path): BlueprintWebClientService.WebClientResponse<String> {
290 val convertedHeaders: Array<BasicHeader> = convertToBasicHeaders(defaultHeaders())
291 val httpPost = HttpPost(host(path))
292 val entity = EntityBuilder.create().setBinary(Files.readAllBytes(filePath)).build()
293 httpPost.setEntity(entity)
294 RestLoggerService.httpInvoking(convertedHeaders)
295 httpPost.setHeaders(convertedHeaders)
296 return performHttpCall(httpPost)
301 @get:JsonProperty("rb-name")
302 var rbName: String? = null
303 @get:JsonProperty("rb-version")
304 var rbVersion: String? = null
305 @get:JsonProperty("profile-name")
306 var profileName: String? = null
307 @get:JsonProperty("namespace")
308 var namespace: String? = "default"
310 override fun toString(): String {
311 return "$rbName:$rbVersion:$profileName"
314 override fun equals(other: Any?): Boolean {
315 if (this === other) return true
316 if (javaClass != other?.javaClass) return false
320 override fun hashCode(): Int {
321 return javaClass.hashCode()