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.functions.resource.resolution.storedContentFromResolvedArtifactNB
23 import org.onap.ccsdk.cds.blueprintsprocessor.rest.BasicAuthRestClientProperties
24 import org.onap.ccsdk.cds.blueprintsprocessor.rest.RestClientProperties
25 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BasicAuthRestClientService
26 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
27 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.RestLoggerService
28 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
29 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
30 import org.apache.commons.io.IOUtils
31 import org.apache.commons.io.FilenameUtils
32 import org.apache.http.client.entity.EntityBuilder
33 import org.apache.http.entity.ContentType
34 import org.apache.http.message.BasicHeader
35 import org.apache.http.client.methods.HttpPost
36 import org.apache.http.client.methods.HttpUriRequest
37 import org.apache.http.client.ClientProtocolException
38 import org.slf4j.LoggerFactory
39 import org.springframework.http.HttpMethod
40 import org.springframework.web.client.RestTemplate
41 import com.fasterxml.jackson.annotation.JsonIgnore
42 import com.fasterxml.jackson.annotation.JsonProperty
43 import java.util.ArrayList
44 import java.io.IOException
46 import java.nio.file.Files
47 import java.nio.file.Paths
48 import java.nio.file.Path
49 import org.springframework.http.HttpHeaders
50 import org.springframework.http.MediaType
51 import java.nio.charset.Charset
52 import java.util.Base64
53 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
55 import java.io.BufferedInputStream
56 import java.io.FileInputStream
57 import java.io.FileOutputStream
58 import java.util.zip.GZIPInputStream
60 open class K8sProfileUpload : AbstractScriptComponentFunction() {
62 private val log = LoggerFactory.getLogger(K8sProfileUpload::class.java)!!
64 override fun getName(): String {
65 return "K8sProfileUpload"
68 override suspend fun processNB(executionRequest: ExecutionServiceInput) {
69 log.info("executing K8s Profile Upload script")
70 val resolution_key = getDynamicProperties("resolution-key").asText()
71 log.info("resolution_key: $resolution_key")
73 val baseK8sApiUrl = getDynamicProperties("api-access").get("url").asText()
74 val k8sApiUsername = getDynamicProperties("api-access").get("username").asText()
75 val k8sApiPassword = getDynamicProperties("api-access").get("password").asText()
77 val prefixList: ArrayList<String> = getTemplatePrefixList(executionRequest)
79 for (prefix in prefixList) {
80 if (prefix.toLowerCase().equals("vnf"))
83 val payload = storedContentFromResolvedArtifactNB(resolution_key, prefix)
84 log.info("Uploading K8S profile for template prefix $prefix")
86 val payloadObject = JacksonUtils.jsonNode(payload) as ObjectNode
88 val vfModuleModelInvariantUuid: String = getResolvedParameter(payloadObject, "vf_module_model_invariant_uuid")
89 val vfModuleModelUuid: String = getResolvedParameter(payloadObject, "vf_module_model_version")
90 val k8sRbProfileName: String = getResolvedParameter(payloadObject, "k8s-rb-profile-name")
91 val k8sRbProfileNamespace: String = getResolvedParameter(payloadObject, "k8s-rb-profile-namespace")
93 val api = K8sApi(k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleModelInvariantUuid, vfModuleModelUuid)
95 if (!api.hasDefinition()) {
96 throw BluePrintProcessorException("K8s RB Definition (${vfModuleModelInvariantUuid}/${vfModuleModelUuid}) not found ")
99 log.info("k8s-rb-profile-name: $k8sRbProfileName")
100 if (k8sRbProfileName.equals("")) {
101 log.info("Profile Name Not Defined - skipping upload")
103 if (api.hasProfile(k8sRbProfileName)) {
104 log.info("Profile Already Existing - skipping upload")
106 profileFilePath = prepareProfileFile(k8sRbProfileName)
108 var profile = K8sProfile()
109 profile.profileName = k8sRbProfileName
110 profile.rbName = vfModuleModelInvariantUuid
111 profile.rbVersion = vfModuleModelUuid
112 profile.namespace = k8sRbProfileNamespace
113 api.createProfile(profile)
114 api.uploadProfileContent(profile, profileFilePath)
116 log.info("K8s Profile Upload Completed")
122 fun prepareProfileFile(k8sRbProfileName: String): Path {
123 val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
124 val bluePrintBasePath: String = bluePrintContext.rootPath
125 var profileFilePath: Path = Paths.get(bluePrintBasePath.plus(File.separator).plus("Templates").plus(File.separator).plus("k8s-profiles").plus(File.separator).plus("${k8sRbProfileName}.tar.gz"))
126 log.info("Reading K8s profile file: ${profileFilePath}")
128 val profileFile = profileFilePath.toFile()
130 if (!profileFile.exists())
131 throw BluePrintProcessorException("K8s Profile template file ${profileFilePath} does not exists")
133 return profileFilePath
136 fun getTemplatePrefixList(executionRequest: ExecutionServiceInput): ArrayList<String> {
137 val result = ArrayList<String>()
138 for (prefix in executionRequest.payload.get("resource-assignment-request").get("template-prefix").elements())
139 result.add(prefix.asText())
143 fun getResolvedParameter(payload: ObjectNode, keyName: String): String {
144 for (node in payload.get("resource-accumulator-resolved-data").elements()) {
145 if (node.get("param-name").asText().equals(keyName)) {
146 return node.get("param-value").asText()
152 override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
153 log.info("Executing Recovery")
154 bluePrintRuntimeService.getBluePrintError().addError("${runtimeException.message}")
157 inner class K8sApi(val username: String, val password: String, val baseUrl: String, val definition: String,
158 val definitionVersion: String) {
159 private val service: UploadFileRestClientService //BasicAuthRestClientService
162 var mapOfHeaders = hashMapOf<String, String>()
163 mapOfHeaders.put("Accept", "application/json")
164 mapOfHeaders.put("Content-Type", "application/json")
165 mapOfHeaders.put("cache-control", " no-cache")
166 mapOfHeaders.put("Accept", "application/json")
167 var basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
168 basicAuthRestClientProperties.username = username
169 basicAuthRestClientProperties.password = password
170 basicAuthRestClientProperties.url = "$baseUrl/api/multicloud-k8s/v1/v1/rb/definition/${definition}/${definitionVersion}"
171 basicAuthRestClientProperties.additionalHeaders = mapOfHeaders
173 this.service = UploadFileRestClientService(basicAuthRestClientProperties)
176 fun hasDefinition(): Boolean {
178 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name, "", "")
180 if (result.status >= 200 && result.status < 300)
184 } catch (e: Exception) {
185 log.info("Caught exception trying to get k8s rb definition")
186 throw BluePrintProcessorException("${e.message}")
190 fun hasProfile(profileName: String): Boolean {
192 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name,
193 "/profile/$profileName", "")
194 if (result.status >= 200 && result.status < 300)
200 } catch (e: Exception) {
201 log.info("Caught exception trying to get k8s rb profile")
202 throw BluePrintProcessorException("${e.message}")
206 fun createProfile(profile: K8sProfile) {
207 val objectMapper = ObjectMapper()
208 val profileJsonString: String = objectMapper.writeValueAsString(profile)
210 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.POST.name,
211 "/profile", profileJsonString)
212 if (result.status < 200 || result.status >= 300) {
213 throw Exception(result.body)
215 } catch (e: Exception) {
216 log.info("Caught exception trying to create k8s rb profile ${profile.profileName}")
217 throw BluePrintProcessorException("${e.message}")
221 fun uploadProfileContent(profile: K8sProfile, filePath: Path) {
223 val result: BlueprintWebClientService.WebClientResponse<String> = service.uploadBinaryFile(
224 "/profile/${profile.profileName}/content", filePath)
225 if (result.status < 200 || result.status >= 300) {
226 throw Exception(result.body)
228 } catch (e: Exception) {
229 log.info("Caught exception trying to upload k8s rb profile ${profile.profileName}")
230 throw BluePrintProcessorException("${e.message}")
236 class UploadFileRestClientService(
237 private val restClientProperties:
238 BasicAuthRestClientProperties
239 ) : BlueprintWebClientService {
241 override fun defaultHeaders(): Map<String, String> {
243 val encodedCredentials = setBasicAuth(
244 restClientProperties.username,
245 restClientProperties.password
248 HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
249 HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,
250 HttpHeaders.AUTHORIZATION to "Basic $encodedCredentials"
254 override fun host(uri: String): String {
255 return restClientProperties.url + uri
258 override fun convertToBasicHeaders(headers: Map<String, String>):
260 val customHeaders: MutableMap<String, String> = headers.toMutableMap()
261 // inject additionalHeaders
262 customHeaders.putAll(verifyAdditionalHeaders(restClientProperties))
264 if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
265 val encodedCredentials = setBasicAuth(
266 restClientProperties.username,
267 restClientProperties.password
269 customHeaders[HttpHeaders.AUTHORIZATION] =
270 "Basic $encodedCredentials"
272 return super.convertToBasicHeaders(customHeaders)
275 private fun setBasicAuth(username: String, password: String): String {
276 val credentialsString = "$username:$password"
277 return Base64.getEncoder().encodeToString(
278 credentialsString.toByteArray(Charset.defaultCharset())
282 @Throws(IOException::class, ClientProtocolException::class)
283 private fun performHttpCall(httpUriRequest: HttpUriRequest): BlueprintWebClientService.WebClientResponse<String> {
284 val httpResponse = httpClient().execute(httpUriRequest)
285 val statusCode = httpResponse.statusLine.statusCode
286 httpResponse.entity.content.use {
287 val body = IOUtils.toString(it, Charset.defaultCharset())
288 return BlueprintWebClientService.WebClientResponse(statusCode, body)
292 fun uploadBinaryFile(path: String, filePath: Path): BlueprintWebClientService.WebClientResponse<String> {
293 val convertedHeaders: Array<BasicHeader> = convertToBasicHeaders(defaultHeaders())
294 val httpPost = HttpPost(host(path))
295 val entity = EntityBuilder.create().setBinary(Files.readAllBytes(filePath)).build()
296 httpPost.setEntity(entity)
297 RestLoggerService.httpInvoking(convertedHeaders)
298 httpPost.setHeaders(convertedHeaders)
299 return performHttpCall(httpPost)
304 @get:JsonProperty("rb-name")
305 var rbName: String? = null
306 @get:JsonProperty("rb-version")
307 var rbVersion: String? = null
308 @get:JsonProperty("profile-name")
309 var profileName: String? = null
310 @get:JsonProperty("namespace")
311 var namespace: String? = "default"
313 override fun toString(): String {
314 return "$rbName:$rbVersion:$profileName"
317 override fun equals(other: Any?): Boolean {
318 if (this === other) return true
319 if (javaClass != other?.javaClass) return false
323 override fun hashCode(): Int {
324 return javaClass.hashCode()