2 * Copyright © 2019 TechMahindra
3 * Author: Vamshi Namilikonda <vn00480215@techmahindra.com>
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 org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
20 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
21 import org.slf4j.LoggerFactory
22 import com.fasterxml.jackson.databind.node.ObjectNode
23 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
24 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
25 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
26 import java.nio.file.Path
27 import org.springframework.http.HttpMethod
28 import org.onap.ccsdk.cds.blueprintsprocessor.rest.BasicAuthRestClientProperties
29 import org.springframework.http.HttpHeaders
30 import org.springframework.http.MediaType
31 import org.apache.http.message.BasicHeader
32 import java.util.Base64
33 import java.nio.charset.Charset
34 import java.io.IOException
35 import org.apache.http.client.methods.HttpUriRequest
36 import com.fasterxml.jackson.annotation.JsonProperty
37 import org.apache.commons.io.IOUtils
38 import org.apache.http.client.methods.HttpPost
39 import org.apache.http.client.entity.EntityBuilder
40 import java.nio.file.Files
41 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.RestLoggerService
42 import org.apache.http.client.ClientProtocolException
43 import com.fasterxml.jackson.databind.ObjectMapper
45 import com.google.gson.Gson
46 import com.google.gson.reflect.TypeToken
48 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.storedContentFromResolvedArtifactNB
49 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BasicAuthRestClientService
51 open class KotlinK8sUpdateConfig : AbstractScriptComponentFunction() {
53 private val log = LoggerFactory.getLogger(KotlinK8sUpdateConfig::class.java)!!
55 override fun getName(): String {
56 return "KotlinK8sUpdateConfig"
59 override suspend fun processNB(executionRequest: ExecutionServiceInput) {
61 println("Exeuting processNB")
62 log.info("Executing processNB from Kotlin script: KotlinK8sUpdateConfig ...")
64 // read the config input
65 val baseK8sApiUrl = getDynamicProperties("api-access").get("url").asText()
66 val k8sApiUsername = getDynamicProperties("api-access").get("username").asText()
67 val k8sApiPassword = getDynamicProperties("api-access").get("password").asText()
69 val prefix = "baseconfigput"
71 val aaiApiUrl = getDynamicProperties("aai-access").get("url").asText()
72 val aaiApiUsername = getDynamicProperties("aai-access").get("username").asText()
73 val aaiApiPassword = getDynamicProperties("aai-access").get("password").asText()
75 log.info("AAI params $aaiApiUrl")
77 val resolution_key = getDynamicProperties("resolution-key").asText()
79 val payload = storedContentFromResolvedArtifactNB(resolution_key, prefix)
81 val payloadObject = JacksonUtils.jsonNode(payload) as ObjectNode
83 val serviceInstanceID: String = getResolvedParameter(payloadObject, "service-instance-id")
84 val vnfID: String = getResolvedParameter(payloadObject, "vnf-id")
86 log.info("Get serviceInstanceID $serviceInstanceID")
87 log.info("Get vnfID $vnfID")
89 val vnfUrl = aaiApiUrl + "/aai/v19/network/generic-vnfs/generic-vnf/" + vnfID + "/vf-modules";
91 val mapOfHeaders = hashMapOf<String, String>()
92 mapOfHeaders.put("Accept", "application/json")
93 mapOfHeaders.put("Content-Type", "application/json")
94 mapOfHeaders.put("x-FromAppId", "SO")
95 mapOfHeaders.put("X-TransactionId", "get_aai_subscr")
96 val basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
97 basicAuthRestClientProperties.username = aaiApiUsername
98 basicAuthRestClientProperties.password = aaiApiPassword
99 basicAuthRestClientProperties.url = vnfUrl
100 basicAuthRestClientProperties.additionalHeaders =mapOfHeaders
101 val basicAuthRestClientService: BasicAuthRestClientService= BasicAuthRestClientService(basicAuthRestClientProperties)
103 val resultOfGet: BlueprintWebClientService.WebClientResponse<String> = basicAuthRestClientService.exchangeResource(HttpMethod.GET.name, "", "")
105 val aaiBody = resultOfGet.body
106 val aaiPayloadObject = JacksonUtils.jsonNode(aaiBody) as ObjectNode
108 for (item in aaiPayloadObject.get("vf-module")) {
110 log.info("item payload Deatils : $item")
112 val isItBaseVfModule = item.get("is-base-vf-module").asText()
114 if(isItBaseVfModule.toBoolean())
117 val vfModuleID: String = item.get("vf-module-id").asText()
119 log.info("AAI Vf-module ID is : $vfModuleID")
121 val vfModuleModelInvariantUuid: String = item.get("model-invariant-id").asText()
123 log.info("AAI Vf-module Invariant ID is : $vfModuleModelInvariantUuid")
125 val vfModuleModelUuid: String = item.get("model-customization-id").asText()
127 log.info("AAI Vf-module UUID is : $vfModuleModelUuid")
129 val vfModuleInstance: String = item.get("heat-stack-id").asText()
131 log.info("AAI Vf-module Heat Stack ID : $vfModuleInstance")
133 val profileName: String = "profile-"+ vfModuleID
134 val templateName: String = "template_" + vfModuleID
136 val randomString = getRandomString(6)
137 val configName: String = "config_"+ randomString
139 var supportedNssai: String = getResolvedParameter(payloadObject, "supportedNssai")
141 log.info("supportedNssai from SO -> "+ supportedNssai)
142 log.info("configName ->"+ configName)
143 log.info("profileName ->"+ profileName)
144 log.info("templateName ->"+ templateName)
147 executeK8sAPI(supportedNssai, k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleModelInvariantUuid, vfModuleModelUuid, templateName, configName, profileName)
151 catch (e: Exception) {
152 log.info("Caught exception trying to get the vnf Details!!")
153 throw BluePrintProcessorException("${e.message}")
157 fun getRandomString(length: Int) : String {
158 val charset = "0123456789"
160 .map { charset.random() }
164 fun executeK8sAPI(supportedNssai: String, k8sApiUsername:String, k8sApiPassword:String, baseK8sApiUrl:String, vfModuleModelInvariantUuid:String, vfModuleModelUuid: String, templateName: String, configName:String, profileName:String){
166 println("Executing executeK8sAPI ...")
168 // read and convert supportedNssai parameters from string to json
169 val sNssaiAsJsonObj = parseSupportedNssai(supportedNssai)
171 // contruct config api
172 val api = K8sConfigApi(k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleModelInvariantUuid, vfModuleModelUuid)
176 var config = K8sConfigPayloadJson()
177 config.templateName = templateName
178 config.configName = configName
179 config.values = Config()
180 config.values.supportedNssai = SupportedNssai()
181 config.values.supportedNssai.snssaiInitial = SnssaiInitial()
182 config.values.supportedNssai.snssaiInitial.snssaiSecond = SnssaiSecond()
183 config.values.supportedNssai.snssaiInitial.snssaiSecond.snssaiFinalArray = Array<SnssaiFinal>(sNssaiAsJsonObj.size){i-> SnssaiFinal()}
185 val dest = buildSNssaiArray(config.values.supportedNssai.snssaiInitial.snssaiSecond.snssaiFinalArray, sNssaiAsJsonObj)
186 api.createOrUpdateConfig(config, profileName)
188 log.info("K8s Configurations create or update Completed")
192 fun buildSNssaiArray(payloadSnssai: Array<SnssaiFinal>, requestSnssai: Array<SnssaiFinal>): Array<SnssaiFinal>{
194 System.arraycopy(requestSnssai, 0, payloadSnssai, 0, requestSnssai.size)
200 fun parseSupportedNssai(supportedNssai: String): Array<SnssaiFinal>{
202 log.info("parsing supportedNssai string..")
204 log.info("sNssai value from input.. $supportedNssai")
206 val trimmed_supportedNssai = supportedNssai.replace("\\s".toRegex(), "").replace("\\r\\n","").replace("\\","")
210 val startInd = trimmed_supportedNssai.indexOf('[')
211 val endInd = trimmed_supportedNssai.indexOf(']')
213 val subStr = trimmed_supportedNssai.substring(startInd, endInd+1)
215 val snType = object : TypeToken<Array<SnssaiFinal>>() {}.type
217 var snList: Array<SnssaiFinal> = gson.fromJson(subStr, snType)
219 log.info("parsing is done.")
225 fun getResolvedParameter(payload: ObjectNode, keyName: String): String {
226 for (node in payload.get("resource-accumulator-resolved-data").elements()) {
227 if (node.get("param-name").asText().equals(keyName)) {
228 return node.get("param-value").asText()
234 override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
235 log.info("Executing Recovery")
238 inner class K8sConfigApi(
239 val username: String,
240 val password: String,
242 val definition: String,
243 val definitionVersion: String
245 private val service: UploadFileConfigClientService // BasicAuthRestClientService
248 var mapOfHeaders = hashMapOf<String, String>()
249 mapOfHeaders.put("Accept", "application/json")
250 mapOfHeaders.put("Content-Type", "application/json")
251 mapOfHeaders.put("cache-control", " no-cache")
252 mapOfHeaders.put("Accept", "application/json")
253 var basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
254 basicAuthRestClientProperties.username = username
255 basicAuthRestClientProperties.password = password
256 basicAuthRestClientProperties.url = "$baseUrl/v1/rb/definition/$definition/$definitionVersion"
257 basicAuthRestClientProperties.additionalHeaders = mapOfHeaders
259 this.service = UploadFileConfigClientService(basicAuthRestClientProperties)
262 fun createOrUpdateConfig(configJson: K8sConfigPayloadJson, profileName: String) {
263 val objectMapper = ObjectMapper()
265 for(snssai in configJson.values.supportedNssai.snssaiInitial.snssaiSecond.snssaiFinalArray){
266 println("snssai->" +snssai.snssai)
267 println("status->"+snssai.status)
271 val configJsonString: String = objectMapper.writeValueAsString(configJson)
273 log.info("payload generated -> "+ configJsonString)
275 val startInd = configJsonString.indexOf('[')
276 val endInd = configJsonString.indexOf(']')
278 val snssaiArray: String = configJsonString.substring(startInd, endInd+1).replace("\"","\\\"").replace("[","\"[").replace("]","]\"")
280 val finalPayload: String = configJsonString.replaceRange(startInd..endInd, snssaiArray)
282 log.info("payload restructured -> "+ finalPayload)
285 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.POST.name,
286 "/profile/${profileName}/config", finalPayload)
287 if (result.status < 200 || result.status >= 300) {
288 throw Exception(result.body)
290 } catch (e: Exception) {
291 log.info("Caught exception trying to create or update configuration ")
292 throw BluePrintProcessorException("${e.message}")
299 class UploadFileConfigClientService(
300 private val restClientProperties:
301 BasicAuthRestClientProperties
302 ) : BlueprintWebClientService {
304 override fun defaultHeaders(): Map<String, String> {
306 val encodedCredentials = setBasicAuth(
307 restClientProperties.username,
308 restClientProperties.password
311 HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
312 HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,
313 HttpHeaders.AUTHORIZATION to "Basic $encodedCredentials"
317 override fun host(uri: String): String {
318 return restClientProperties.url + uri
321 override fun convertToBasicHeaders(headers: Map<String, String>):
323 val customHeaders: MutableMap<String, String> = headers.toMutableMap()
324 // inject additionalHeaders
325 customHeaders.putAll(verifyAdditionalHeaders(restClientProperties))
327 if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
328 val encodedCredentials = setBasicAuth(
329 restClientProperties.username,
330 restClientProperties.password
332 customHeaders[HttpHeaders.AUTHORIZATION] =
333 "Basic $encodedCredentials"
335 return super.convertToBasicHeaders(customHeaders)
338 private fun setBasicAuth(username: String, password: String): String {
339 val credentialsString = "$username:$password"
340 return Base64.getEncoder().encodeToString(
341 credentialsString.toByteArray(Charset.defaultCharset())
345 @Throws(IOException::class, ClientProtocolException::class)
346 private fun performHttpCall(httpUriRequest: HttpUriRequest): BlueprintWebClientService.WebClientResponse<String> {
347 val httpResponse = httpClient().execute(httpUriRequest)
348 val statusCode = httpResponse.statusLine.statusCode
349 httpResponse.entity.content.use {
350 val body = IOUtils.toString(it, Charset.defaultCharset())
351 return BlueprintWebClientService.WebClientResponse(statusCode, body)
355 fun uploadBinaryFile(path: String, filePath: Path): BlueprintWebClientService.WebClientResponse<String> {
356 val convertedHeaders: Array<BasicHeader> = convertToBasicHeaders(defaultHeaders())
357 val httpPost = HttpPost(host(path))
358 val entity = EntityBuilder.create().setBinary(Files.readAllBytes(filePath)).build()
359 httpPost.setEntity(entity)
360 RestLoggerService.httpInvoking(convertedHeaders)
361 httpPost.setHeaders(convertedHeaders)
362 return performHttpCall(httpPost)
367 class K8sConfigPayloadJson {
368 @get:JsonProperty("template-name")
369 var templateName: String? = null
370 @get:JsonProperty("config-name")
371 var configName: String? = null
372 @get:JsonProperty("values")
373 lateinit var values: Config
375 override fun toString(): String {
376 return "$templateName:$configName:$values"
379 override fun equals(other: Any?): Boolean {
380 if (this === other) return true
381 if (javaClass != other?.javaClass) return false
385 override fun hashCode(): Int {
386 return javaClass.hashCode()
391 @get:JsonProperty("config")
392 lateinit var supportedNssai: SupportedNssai
395 class SupportedNssai{
396 @get:JsonProperty("supportedNssai")
397 lateinit var snssaiInitial: SnssaiInitial
402 @get:JsonProperty("sNssai")
403 lateinit var snssaiSecond: SnssaiSecond
408 @get:JsonProperty("snssai")
409 lateinit var snssaiFinalArray: Array<SnssaiFinal>
414 @get:JsonProperty("snssai")
415 var snssai: String? = null
417 @get:JsonProperty("status")
418 var status: String? = null
422 fun main(args: Array<String>) {
424 val supportedNssai = """
426 {\r\n \"sNssai\":[\r\n {\r\n \"snssai\":\"001-100001\",\r\n \"status\":\"created-modified\"\r\n },\r\n {\r\n \"snssai\":\"002-100001\",\r\n \"status\":\"activated\"\r\n },\r\n {\r\n \"snssai\":\"003-100001\",\r\n \"status\":\"de-activated\"\r\n }\r\n ]\r\n }\r\n
430 val kotlin = KotlinK8sUpdateConfig()
436 vfModuleModelInvariantUuid
442 kotlin.executeK8sAPI(supportedNssai, "admin", "admin", "http://0.0.0.0:9015", "rb_test", "1", "template_test", "config_test", "profile_test")