2 * Copyright © 2019 TechMahindra
3 * Author: Malinconico Aniello Paolo, Vamshi Namilikonda, Thamlur Raju
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.annotation.JsonIgnore
20 import com.fasterxml.jackson.annotation.JsonProperty
21 import com.fasterxml.jackson.databind.ObjectMapper
22 import com.fasterxml.jackson.databind.node.ObjectNode
24 import java.nio.file.Path
25 import java.nio.file.Paths
26 import org.apache.commons.io.FilenameUtils
27 import org.apache.commons.io.IOUtils
28 import org.apache.http.client.ClientProtocolException
29 import org.apache.http.client.entity.EntityBuilder
30 import org.apache.http.client.methods.HttpPost
31 import org.apache.http.client.methods.HttpUriRequest
32 import org.apache.http.entity.ContentType
33 import org.apache.http.message.BasicHeader
34 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
35 import org.onap.ccsdk.cds.blueprintsprocessor.rest.BasicAuthRestClientProperties
36 import org.onap.ccsdk.cds.blueprintsprocessor.rest.RestClientProperties
37 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BasicAuthRestClientService
38 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
39 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.RestLoggerService
40 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
41 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
42 import org.onap.ccsdk.cds.controllerblueprints.core.utils.ArchiveType
43 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintArchiveUtils
44 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
45 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.storedContentFromResolvedArtifactNB
46 import org.slf4j.LoggerFactory
47 import org.springframework.http.HttpHeaders
48 import org.springframework.http.HttpMethod
49 import org.springframework.http.MediaType
50 import org.springframework.web.client.RestTemplate
51 import org.yaml.snakeyaml.Yaml
52 import java.util.ArrayList
53 import java.io.IOException
54 import java.util.Base64
55 import java.nio.charset.Charset
56 import java.nio.file.Files
57 import com.google.gson.Gson
58 import com.google.gson.reflect.TypeToken
60 open class DayOneConfig : AbstractScriptComponentFunction() {
62 private val log = LoggerFactory.getLogger(DayOneConfig::class.java)!!
64 override fun getName(): String {
68 override suspend fun processNB(executionRequest: ExecutionServiceInput) {
69 log.info("DAY-1 Script excution Started")
71 val prefix = "baseconfig"
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 log.info("Multi-cloud params $baseK8sApiUrl")
79 val aaiApiUrl = getDynamicProperties("aai-access").get("url").asText()
80 val aaiApiUsername = getDynamicProperties("aai-access").get("username").asText()
81 val aaiApiPassword = getDynamicProperties("aai-access").get("password").asText()
83 log.info("AAI params $aaiApiUrl")
85 val resolution_key = getDynamicProperties("resolution-key").asText()
87 val payload = storedContentFromResolvedArtifactNB(resolution_key, prefix)
89 val payloadObject = JacksonUtils.jsonNode(payload) as ObjectNode
91 val serviceInstanceID: String = getResolvedParameter(payloadObject, "service-instance-id")
92 val vnfID: String = getResolvedParameter(payloadObject, "vnf-id")
94 log.info("Get serviceInstanceID $serviceInstanceID")
95 log.info("Get vnfID $vnfID")
97 val vnfUrl = aaiApiUrl + "/aai/v19/network/generic-vnfs/generic-vnf/" + vnfID + "/vf-modules";
99 val mapOfHeaders = hashMapOf<String, String>()
100 mapOfHeaders.put("Accept", "application/json")
101 mapOfHeaders.put("Content-Type", "application/json")
102 mapOfHeaders.put("x-FromAppId", "SO")
103 mapOfHeaders.put("X-TransactionId", "get_aai_subscr")
104 val basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
105 basicAuthRestClientProperties.username = aaiApiUsername
106 basicAuthRestClientProperties.password = aaiApiPassword
107 basicAuthRestClientProperties.url = vnfUrl
108 basicAuthRestClientProperties.additionalHeaders =mapOfHeaders
109 val basicAuthRestClientService: BasicAuthRestClientService= BasicAuthRestClientService(basicAuthRestClientProperties)
111 val resultOfGet: BlueprintWebClientService.WebClientResponse<String> = basicAuthRestClientService.exchangeResource(HttpMethod.GET.name, "", "")
113 val aaiBody = resultOfGet.body
114 val aaiPayloadObject = JacksonUtils.jsonNode(aaiBody) as ObjectNode
116 for (item in aaiPayloadObject.get("vf-module")) {
118 log.info("item payload Deatils : $item")
120 val isItBaseVfModule = item.get("is-base-vf-module").asText()
122 if(isItBaseVfModule.toBoolean())
125 val vfModuleID: String = item.get("vf-module-id").asText()
127 log.info("AAI Vf-module ID is : $vfModuleID")
129 val vfModuleInvariantID: String = item.get("model-invariant-id").asText()
131 log.info("AAI Vf-module Invariant ID is : $vfModuleInvariantID")
133 val vfModuleUUID: String = item.get("model-version-id").asText()
135 log.info("AAI Vf-module UUID is : $vfModuleUUID")
137 val vfModuleInstance: String = item.get("heat-stack-id").asText()
139 log.info("AAI Vf-module Heat Stack ID : $vfModuleInstance")
143 val Instance = vfModuleInstance.split(delimiter)
144 val instanceName = Instance[0]
145 val instanceID = Instance[1]
146 log.info("instance name is : $instanceName")
147 log.info("K8S instance ID is : $instanceID")
149 val instanceNameNameArray: List<String> = instanceName.split("..")
150 val typOfVfmodule = instanceNameNameArray[1]
151 log.info("Type of vf-module: $typOfVfmodule")
153 val k8sRbProfileName: String = "profile_" + vfModuleID
155 val k8sConfigTemplateName: String = "template_" + vfModuleID
157 val api = K8sConfigTemplateApi(k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleInvariantID, vfModuleUUID, k8sConfigTemplateName)
159 // Check if definition exists
160 if (!api.hasDefinition()) {
161 throw BluePrintProcessorException("K8s Config Template ($vfModuleInvariantID/$vfModuleUUID) - $k8sConfigTemplateName not found ")
164 log.info("Config Template name: $k8sConfigTemplateName")
166 if (k8sRbProfileName.equals("")) {
167 throw BluePrintProcessorException("K8s rb profile name is empty! Either define profile name to use or choose default")
170 var configTemplate = K8sConfigTemplate()
171 configTemplate.templateName = k8sConfigTemplateName
172 configTemplate.description = " "
173 configTemplate.ChartName = typOfVfmodule
174 log.info("Chart name: ${configTemplate.ChartName}")
176 val instanceAPI = K8sInstanceApi(k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleInvariantID, vfModuleUUID)
177 val configMapName: String = instanceAPI.getInsnceDetails(instanceID, typOfVfmodule)
179 log.info("configmap retrieved " +typOfVfmodule+ "vfmodule ->"+ configMapName)
180 modifyTemplate(configMapName, typOfVfmodule)
182 val configTemplateFile: Path = prepareConfigTemplateJson(k8sConfigTemplateName, typOfVfmodule)
184 if (!api.hasConfigTemplate(configTemplate)) {
185 log.info("K8s Config Template Upload Started")
186 api.createConfigTemplate(configTemplate)
187 api.uploadConfigTemplateContent(configTemplate, configTemplateFile)
188 log.info("K8s Config Template Upload Completed")
191 log.info("DAY-1 Script excution completed")
193 catch (e: Exception) {
194 log.info("Caught exception trying to get the vnf Details!!")
195 // throw BluePrintProcessorException("${e.message}")
199 fun prepareConfigTemplateJson(configTemplateName: String, typOfVfmodule: String): Path {
200 val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
201 val bluePrintBasePath: String = bluePrintContext.rootPath
203 var profileFilePath: Path = Paths.get(bluePrintBasePath.plus(File.separator).plus("Templates").plus(File.separator).plus("k8s-profiles").plus(File.separator).plus(typOfVfmodule +"-config-template.tar.gz"))
204 log.info("Reading K8s Config Template file: $profileFilePath")
206 val profileFile = profileFilePath.toFile()
208 if (!profileFile.exists())
209 throw BluePrintProcessorException("K8s Profile template file $profileFilePath does not exists")
211 return profileFilePath
214 fun getResolvedParameter(payload: ObjectNode, keyName: String): String {
215 for (node in payload.get("resource-accumulator-resolved-data").elements()) {
216 if (node.get("param-name").asText().equals(keyName)) {
217 return node.get("param-value").asText()
222 override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
223 log.info("Executing Recovery")
224 bluePrintRuntimeService.getBluePrintError().addError("${runtimeException.message}")
227 fun modifyTemplate(configmapName: String, typOfVfmodule: String): String {
229 log.info("Executing modifyTemplate ->")
231 val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
232 val bluePrintBasePath: String = bluePrintContext.rootPath
234 val destPath: String = "/tmp/config-template-"+typOfVfmodule
236 var templateFilePath: Path = Paths.get(bluePrintBasePath.plus(File.separator).plus("Templates").plus(File.separator).plus("k8s-profiles").plus(File.separator).plus(typOfVfmodule +"-config-template.tar.gz"))
238 log.info("Reading config template file: ${templateFilePath}")
239 val templateFile = templateFilePath.toFile()
241 if (!templateFile.exists())
242 throw BluePrintProcessorException("K8s Profile template file ${templateFilePath} does not exists")
244 log.info("Decompressing config template to ${destPath}")
246 val decompressedProfile: File = BluePrintArchiveUtils.deCompress(templateFilePath.toFile(),
247 "${destPath}", ArchiveType.TarGz)
249 log.info("${templateFilePath.toString()} decompression completed")
251 //Here we update override.yaml file
253 val manifestFileName = destPath.plus(File.separator).plus(typOfVfmodule).plus(File.separator).plus("templates").plus(File.separator).plus("configmap.yaml")
254 log.info("Modification of configmap.yaml file at ${manifestFileName.toString()}")
255 var finalManifest = ""
256 File(manifestFileName).bufferedReader().use { inr ->
257 val manifestYaml = Yaml()
258 val manifestObject: Map<String, Any> = manifestYaml.load(inr)
260 for((k,v) in manifestObject) {
261 log.info("manifestObject: ${k}, ${v}" )
264 log.info("Uploaded YAML object")
266 val metadata: MutableMap<String, Any> = manifestObject.get("metadata") as MutableMap<String, Any>
267 log.info("Uploaded config YAML object")
269 for((k,v) in metadata) {
270 metadata.put(k, configmapName)
273 finalManifest = manifestYaml.dump(manifestObject)
276 File(manifestFileName).bufferedWriter().use { out -> out.write(finalManifest) }
278 log.info(finalManifest)
280 log.info("Reading config template file: ${templateFilePath}")
282 if (!templateFile.exists())
283 throw BluePrintProcessorException("config template file ${templateFilePath} does not exists")
285 val tempMainPath: File = createTempDir("config-template-", "")
286 val tempConfigTemplatePath: File = createTempDir("conftemplate-", "", tempMainPath)
287 log.info("Decompressing profile to ${tempConfigTemplatePath.toString()}")
289 val decompressedProfile2: File = BluePrintArchiveUtils.deCompress(templateFilePath.toFile(),
290 "${tempConfigTemplatePath.toString()}", ArchiveType.TarGz)
292 log.info("${templateFilePath.toString()} decompression completed")
294 //Here we update configmap.yaml file
296 log.info("Modification of configmap.yaml file ")
297 val manifestFileName2 = destPath.toString().plus(File.separator).plus(typOfVfmodule).plus(File.separator).plus("templates").plus(File.separator).plus("configmap.yaml")
298 val destOverrideFile = tempConfigTemplatePath.toString().plus(File.separator).plus(typOfVfmodule).plus(File.separator).plus("templates").plus(File.separator).plus("configmap.yaml")
299 log.info("destination override file ${destOverrideFile}")
301 File(manifestFileName2).copyTo(File(destOverrideFile), true)
303 if (!BluePrintArchiveUtils.compress(decompressedProfile2, templateFilePath.toFile(),
304 ArchiveType.TarGz)) {
305 throw BluePrintProcessorException("Profile compression has failed")
308 log.info("${templateFilePath.toString()} compression completed")
313 inner class K8sInstanceApi(
314 val username: String,
315 val password: String,
317 val definition: String,
318 val definitionVersion: String
320 private val service: UploadConfigTemplateRestClientService // BasicAuthRestClientService
323 var mapOfHeaders = hashMapOf<String, String>()
324 mapOfHeaders.put("Accept", "application/json")
325 mapOfHeaders.put("Content-Type", "application/json")
326 mapOfHeaders.put("cache-control", " no-cache")
327 mapOfHeaders.put("Accept", "application/json")
328 var basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
329 basicAuthRestClientProperties.username = username
330 basicAuthRestClientProperties.password = password
331 basicAuthRestClientProperties.url = "$baseUrl/v1/instance"
332 basicAuthRestClientProperties.additionalHeaders = mapOfHeaders
334 this.service = UploadConfigTemplateRestClientService(basicAuthRestClientProperties)
337 fun getInsnceDetails(instanceId: String, vfModuleType: String): String {
338 log.info("Executing K8sInstanceApi.getInsnceDetails")
340 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name, "/${instanceId}", "")
342 if (result.status >= 200 && result.status < 300) {
343 log.info("K8s instance details retrieved, processing it for configmap details")
344 log.info("response body -> "+result.body.toString())
345 val cmName: String = processInstanceResponse(result.body, vfModuleType)
349 } catch (e: Exception) {
350 log.info("Caught exception trying to get k8s instance details")
351 throw BluePrintProcessorException("${e.message}")
355 fun processInstanceResponse(response: String, vfModuleType: String): String {
357 log.info("K8s instance details retrieved, processing it for configmap details")
361 val startInd = response.indexOf('[')
362 val endInd = response.indexOf(']')
364 val subStr = response.substring(startInd, endInd+1)
366 val resourceType = object : TypeToken<Array<K8sResources>>() {}.type
368 var resources: Array<K8sResources> = gson.fromJson(subStr, resourceType)
370 for (resource in resources){
372 if(resource.GVK?.Kind == "ConfigMap" && resource.Name?.contains(vfModuleType)){
385 inner class K8sConfigTemplateApi(
386 val username: String,
387 val password: String,
389 val definition: String,
390 val definitionVersion: String,
391 val configTemplateName: String
393 private val service: UploadConfigTemplateRestClientService // BasicAuthRestClientService
396 var mapOfHeaders = hashMapOf<String, String>()
397 mapOfHeaders.put("Accept", "application/json")
398 mapOfHeaders.put("Content-Type", "application/json")
399 mapOfHeaders.put("cache-control", " no-cache")
400 mapOfHeaders.put("Accept", "application/json")
401 var basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
402 basicAuthRestClientProperties.username = username
403 basicAuthRestClientProperties.password = password
404 basicAuthRestClientProperties.url = "$baseUrl/v1/rb/definition/$definition/$definitionVersion"
405 basicAuthRestClientProperties.additionalHeaders = mapOfHeaders
407 this.service = UploadConfigTemplateRestClientService(basicAuthRestClientProperties)
410 fun hasDefinition(): Boolean {
412 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name, "", "")
414 if (result.status >= 200 && result.status < 300)
418 } catch (e: Exception) {
419 log.info("Caught exception trying to get k8s config trmplate definition")
420 throw BluePrintProcessorException("${e.message}")
424 fun hasConfigTemplate(profile: K8sConfigTemplate): Boolean {
426 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name, "/config-template/${profile.templateName}", "")
428 if (result.status >= 200 && result.status < 300) {
429 log.info("ConfigTemplate already exists")
433 } catch (e: Exception) {
434 log.info("Caught exception trying to get k8s config trmplate definition")
435 throw BluePrintProcessorException("${e.message}")
439 fun createConfigTemplate(profile: K8sConfigTemplate) {
440 val objectMapper = ObjectMapper()
441 val profileJsonString: String = objectMapper.writeValueAsString(profile)
443 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(
444 HttpMethod.POST.name,
449 if (result.status >= 200 && result.status < 300) {
450 log.info("Config template json info uploaded correctly")
451 } else if (result.status < 200 || result.status >= 300) {
452 log.info("Config template already exists")
454 } catch (e: Exception) {
455 log.info("Caught exception trying to create k8s config template ${profile.templateName} - updated")
456 // throw BluePrintProcessorException("${e.message}")
460 fun uploadConfigTemplateContent(profile: K8sConfigTemplate, filePath: Path) {
462 val result: BlueprintWebClientService.WebClientResponse<String> = service.uploadBinaryFile(
463 "/config-template/${profile.templateName}/content",
466 if (result.status < 200 || result.status >= 300) {
467 throw Exception(result.body)
469 } catch (e: Exception) {
470 log.info("Caught exception trying to upload k8s config template ${profile.templateName}")
471 throw BluePrintProcessorException("${e.message}")
477 class UploadConfigTemplateRestClientService(
478 private val restClientProperties:
479 BasicAuthRestClientProperties
480 ) : BlueprintWebClientService {
482 override fun defaultHeaders(): Map<String, String> {
484 val encodedCredentials = setBasicAuth(
485 restClientProperties.username,
486 restClientProperties.password
489 HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
490 HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,
491 HttpHeaders.AUTHORIZATION to "Basic $encodedCredentials"
495 override fun host(uri: String): String {
496 return restClientProperties.url + uri
499 override fun convertToBasicHeaders(headers: Map<String, String>):
501 val customHeaders: MutableMap<String, String> = headers.toMutableMap()
502 // inject additionalHeaders
503 customHeaders.putAll(verifyAdditionalHeaders(restClientProperties))
505 if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
506 val encodedCredentials = setBasicAuth(
507 restClientProperties.username,
508 restClientProperties.password
510 customHeaders[HttpHeaders.AUTHORIZATION] =
511 "Basic $encodedCredentials"
513 return super.convertToBasicHeaders(customHeaders)
516 private fun setBasicAuth(username: String, password: String): String {
517 val credentialsString = "$username:$password"
518 return Base64.getEncoder().encodeToString(
519 credentialsString.toByteArray(Charset.defaultCharset())
523 @Throws(IOException::class, ClientProtocolException::class)
524 private fun performHttpCall(httpUriRequest: HttpUriRequest): BlueprintWebClientService.WebClientResponse<String> {
525 val httpResponse = httpClient().execute(httpUriRequest)
526 val statusCode = httpResponse.statusLine.statusCode
527 httpResponse.entity.content.use {
528 val body = IOUtils.toString(it, Charset.defaultCharset())
529 return BlueprintWebClientService.WebClientResponse(statusCode, body)
533 fun uploadBinaryFile(path: String, filePath: Path): BlueprintWebClientService.WebClientResponse<String> {
534 val convertedHeaders: Array<BasicHeader> = convertToBasicHeaders(defaultHeaders())
535 val httpPost = HttpPost(host(path))
536 val entity = EntityBuilder.create().setBinary(Files.readAllBytes(filePath)).build()
537 httpPost.setEntity(entity)
538 RestLoggerService.httpInvoking(convertedHeaders)
539 httpPost.setHeaders(convertedHeaders)
540 return performHttpCall(httpPost)
544 class K8sConfigTemplate {
545 @get:JsonProperty("template-name")
546 var templateName: String? = null
547 @get:JsonProperty("description")
548 var description: String? = null
549 @get:JsonProperty("ChartName")
550 var ChartName: String? = null
552 override fun equals(other: Any?): Boolean {
553 if (this === other) return true
554 if (javaClass != other?.javaClass) return false
558 override fun hashCode(): Int {
559 return javaClass.hashCode()
566 lateinit var Name: String
572 var Group: String? = null
573 var Version: String? = null
574 var Kind: String? = null
578 fun main(args: Array<String>) {
580 val kotlin = DayOneConfig()
582 kotlin.modifyTemplate("modified", "upf")