1. Wrap configuration-template DAY2 API 06/117606/6
authorGrzegorz Wielgosinski <g.wielgosins@samsung.com>
Mon, 8 Feb 2021 18:24:25 +0000 (19:24 +0100)
committerKAPIL SINGAL <ks220y@att.com>
Mon, 15 Feb 2021 13:44:28 +0000 (13:44 +0000)
2. Create config-template component

Issue-ID: CCSDK-2922
Signed-off-by: Grzegorz Wielgosinski <g.wielgosins@samsung.com>
Change-Id: I124c5995294c2c05eab471eb3a2658368a49cf06

components/model-catalog/definition-type/starter-type/node_type/component-k8s-config-template.json [new file with mode: 0644]
ms/blueprintsprocessor/functions/k8s-connection-plugin/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/K8sPluginApi.kt
ms/blueprintsprocessor/functions/k8s-connection-plugin/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/definition/template/K8sConfigTemplateComponent.kt [new file with mode: 0644]
ms/blueprintsprocessor/functions/k8s-connection-plugin/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/definition/template/K8sTemplate.kt [new file with mode: 0644]

diff --git a/components/model-catalog/definition-type/starter-type/node_type/component-k8s-config-template.json b/components/model-catalog/definition-type/starter-type/node_type/component-k8s-config-template.json
new file mode 100644 (file)
index 0000000..452bb15
--- /dev/null
@@ -0,0 +1,65 @@
+{
+  "description": "This component is rendering and sending the k8s config template to the multicloud plugin.",
+  "version": "1.0.0",
+  "attributes": {
+    "statuses": {
+      "required": true,
+      "type": "json"
+    }
+  },
+  "capabilities": {
+    "component-node": {
+      "type": "tosca.capabilities.Node"
+    }
+  },
+  "interfaces": {
+    "K8sConfigTemplateComponent": {
+      "operations": {
+        "process": {
+          "inputs": {
+            "k8s-rb-config-template-name": {
+                "description": "K8s template name",
+                "required": false,
+                "type": "string"
+            },
+            "k8s-rb-definition-name": {
+                "description": "K8s definition name",
+                "required": false,
+                "type": "string"
+            },
+            "k8s-rb-definition-version": {
+                "description": "Version of the definition",
+                "required": false,
+                "type": "string"
+            },
+            "k8s-rb-config-template-source": {
+                "description": "Source (tgz/folder) for the template in CBA",
+                "required": false,
+                "type": "string"
+            },
+            "artifact-prefix-names": {
+              "description": "Resource Assignment Artifact Prefix names",
+              "required": false,
+              "type": "list",
+              "entry_schema": {
+                "type": "string"
+              }
+            },
+            "resource-assignment-map": {
+                "description": "Holds resolved values for each artifact prefix eg. { vdns: { vnf-id: 123 } }",
+                "required": false,
+                "type": "json"
+            }
+          },
+          "outputs": {
+            "statuses": {
+                "required": true,
+                "type": "string"
+            }
+          }
+        }
+      }
+    }
+  },
+  "derived_from": "tosca.nodes.Component"
+}
index 211bc5a..a36c8e3 100644 (file)
@@ -23,9 +23,12 @@ import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.definition.K8sDefinitionRestClient
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.definition.K8sUploadFileRestClientService
 import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.definition.profile.K8sProfile
+import com.fasterxml.jackson.module.kotlin.readValue
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.definition.template.K8sTemplate
 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
 import org.onap.ccsdk.cds.controllerblueprints.core.BlueprintProcessorException
 import org.slf4j.LoggerFactory
+import org.springframework.http.HttpMethod.DELETE
 import org.springframework.http.HttpMethod.GET
 import org.springframework.http.HttpMethod.POST
 import java.nio.file.Path
@@ -117,4 +120,84 @@ class K8sPluginApi(
             throw BlueprintProcessorException("${e.message}")
         }
     }
+
+    fun createTemplate(definition: String, definitionVersion: String, template: K8sTemplate): Boolean {
+        val rbDefinitionService = K8sDefinitionRestClient(k8sConfiguration, definition, definitionVersion)
+        val templateJsonString: String = objectMapper.writeValueAsString(template)
+        try {
+            val result: BlueprintWebClientService.WebClientResponse<String> = rbDefinitionService.exchangeResource(
+                POST.name,
+                "/config-template",
+                templateJsonString
+            )
+            log.debug(result.toString())
+            return result.status in 200..299
+        } catch (e: Exception) {
+            log.error("Caught exception during create template")
+            throw BlueprintProcessorException("${e.message}")
+        }
+    }
+
+    fun uploadTemplate(definition: String, definitionVersion: String, template: K8sTemplate, filePath: Path) {
+        val fileUploadService = K8sUploadFileRestClientService(k8sConfiguration, definition, definitionVersion)
+        try {
+            val result: BlueprintWebClientService.WebClientResponse<String> = fileUploadService.uploadBinaryFile(
+                "/config-template/${template.templateName}/content",
+                filePath
+            )
+            if (result.status !in 200..299) {
+                throw Exception(result.body)
+            }
+        } catch (e: Exception) {
+            log.error("Caught exception trying to upload k8s rb template ${template.templateName}")
+            throw BlueprintProcessorException("${e.message}")
+        }
+    }
+
+    fun deleteTemplate(definition: String, definitionVersion: String, templateName: String) {
+        val rbDefinitionService = K8sDefinitionRestClient(k8sConfiguration, definition, definitionVersion)
+        try {
+            val result: BlueprintWebClientService.WebClientResponse<String> = rbDefinitionService.exchangeResource(
+                DELETE.name,
+                "/config-template/$templateName",
+                ""
+            )
+            log.debug(result.toString())
+        } catch (e: Exception) {
+            log.error("Caught exception during get template")
+            throw BlueprintProcessorException("${e.message}")
+        }
+    }
+
+    fun getTemplate(definition: String, definitionVersion: String, templateName: String): K8sTemplate {
+        val rbDefinitionService = K8sDefinitionRestClient(k8sConfiguration, definition, definitionVersion)
+        try {
+            val result: BlueprintWebClientService.WebClientResponse<String> = getTemplateRequest(rbDefinitionService, templateName)
+            log.debug(result.toString())
+            return objectMapper.readValue(result.body)
+        } catch (e: Exception) {
+            log.error("Caught exception during get template")
+            throw BlueprintProcessorException("${e.message}")
+        }
+    }
+
+    fun hasTemplate(definition: String, definitionVersion: String, templateName: String): Boolean {
+        val rbDefinitionService = K8sDefinitionRestClient(k8sConfiguration, definition, definitionVersion)
+        try {
+            val result: BlueprintWebClientService.WebClientResponse<String> = getTemplateRequest(rbDefinitionService, templateName)
+            log.debug(result.toString())
+            return result.status in 200..299
+        } catch (e: Exception) {
+            log.error("Caught exception during get template")
+            throw BlueprintProcessorException("${e.message}")
+        }
+    }
+
+    private fun getTemplateRequest(rbDefinitionService: K8sDefinitionRestClient, templateName: String): BlueprintWebClientService.WebClientResponse<String> {
+        return rbDefinitionService.exchangeResource(
+            GET.name,
+            "/config-template/$templateName",
+            ""
+        )
+    }
 }
diff --git a/ms/blueprintsprocessor/functions/k8s-connection-plugin/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/definition/template/K8sConfigTemplateComponent.kt b/ms/blueprintsprocessor/functions/k8s-connection-plugin/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/definition/template/K8sConfigTemplateComponent.kt
new file mode 100644 (file)
index 0000000..1156e1b
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ * Modifications Copyright © 2019 IBM.
+ * Modifications Copyright © 2020 Orange.
+ * Modifications Copyright © 2020 Deutsche Telekom AG.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.definition.template
+
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.node.ArrayNode
+import com.fasterxml.jackson.databind.node.ObjectNode
+import org.apache.commons.io.FileUtils
+import org.onap.ccsdk.cds.blueprintsprocessor.core.BlueprintPropertiesService
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.K8sConnectionPluginConfiguration
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.K8sPluginApi
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants
+import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.ResourceResolutionService
+import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction
+import org.onap.ccsdk.cds.controllerblueprints.core.BlueprintConstants
+import org.onap.ccsdk.cds.controllerblueprints.core.BlueprintProcessorException
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonNode
+import org.onap.ccsdk.cds.controllerblueprints.core.data.ArtifactDefinition
+import org.onap.ccsdk.cds.controllerblueprints.core.returnNullIfMissing
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BlueprintVelocityTemplateService
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.ArchiveType
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.BlueprintArchiveUtils
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
+import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
+import org.slf4j.LoggerFactory
+import org.springframework.beans.factory.config.ConfigurableBeanFactory
+import org.springframework.context.annotation.Scope
+import org.springframework.stereotype.Component
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+
+@Component("component-k8s-config-template")
+@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+open class K8sConfigTemplateComponent(
+    private var bluePrintPropertiesService: BlueprintPropertiesService,
+    private val resourceResolutionService: ResourceResolutionService
+) :
+
+    AbstractComponentFunction() {
+
+    companion object {
+        const val INPUT_K8S_DEFINITION_NAME = "k8s-rb-definition-name"
+        const val INPUT_K8S_DEFINITION_VERSION = "k8s-rb-definition-version"
+        const val INPUT_K8S_TEMPLATE_NAME = "k8s-rb-config-template-name"
+        const val INPUT_K8S_TEMPLATE_SOURCE = "k8s-rb-config-template-source"
+        const val INPUT_RESOURCE_ASSIGNMENT_MAP = "resource-assignment-map"
+        const val INPUT_ARTIFACT_PREFIX_NAMES = "artifact-prefix-names"
+
+        const val OUTPUT_STATUSES = "statuses"
+        const val OUTPUT_SKIPPED = "skipped"
+        const val OUTPUT_UPLOADED = "uploaded"
+        const val OUTPUT_ERROR = "error"
+    }
+
+    private val log = LoggerFactory.getLogger(K8sConfigTemplateComponent::class.java)!!
+
+    override suspend fun processNB(executionRequest: ExecutionServiceInput) {
+        log.info("Triggering K8s Profile Upload component logic.")
+
+        val inputParameterNames = arrayOf(
+            INPUT_K8S_TEMPLATE_NAME,
+            INPUT_K8S_DEFINITION_NAME,
+            INPUT_K8S_DEFINITION_VERSION,
+            INPUT_K8S_TEMPLATE_SOURCE,
+            INPUT_ARTIFACT_PREFIX_NAMES
+        )
+        val outputPrefixStatuses = mutableMapOf<String, String>()
+        val inputParamsMap = mutableMapOf<String, JsonNode?>()
+
+        inputParameterNames.forEach {
+            inputParamsMap[it] = getOptionalOperationInput(it)?.returnNullIfMissing()
+        }
+
+        log.info("Getting the template prefixes")
+        val prefixList: ArrayList<String> = getTemplatePrefixList(inputParamsMap[INPUT_ARTIFACT_PREFIX_NAMES])
+
+        log.info("Iterating over prefixes in resource assignment map.")
+        for (prefix in prefixList) {
+            // Prefilling prefix sucess status
+            outputPrefixStatuses[prefix] = OUTPUT_SKIPPED
+            // Resource assignment map is organized by prefixes, in each iteraton we work only
+            // on one section of resource assignment map
+            val prefixNode: JsonNode = operationInputs[INPUT_RESOURCE_ASSIGNMENT_MAP]?.get(prefix) ?: continue
+            val assignmentMapPrefix = JacksonUtils.jsonNode(prefixNode.toPrettyString()) as ObjectNode
+
+            // We are copying the map because for each prefix it might be completed with a different data
+            val prefixInputParamsMap = inputParamsMap.toMutableMap()
+            prefixInputParamsMap.forEach { (inputParamName, value) ->
+                if (value == null) {
+                    val mapValue = assignmentMapPrefix.get(inputParamName)
+                    log.debug("$inputParamName value was $value so we fetch $mapValue")
+                    prefixInputParamsMap[inputParamName] = mapValue
+                }
+            }
+
+            // For clarity we pull out the required fields
+            val templateName: String? = prefixInputParamsMap[INPUT_K8S_TEMPLATE_NAME]?.returnNullIfMissing()?.asText()
+            val definitionName: String? = prefixInputParamsMap[INPUT_K8S_DEFINITION_NAME]?.returnNullIfMissing()?.asText()
+            val definitionVersion: String? = prefixInputParamsMap[INPUT_K8S_DEFINITION_VERSION]?.returnNullIfMissing()?.asText()
+
+            // rename after commit
+            val k8sConnectionPluginConfiguration = K8sConnectionPluginConfiguration(bluePrintPropertiesService)
+
+            // Creating API connector
+            val api = K8sPluginApi(k8sConnectionPluginConfiguration)
+            if ((templateName == null) || (definitionName == null) || (definitionVersion == null)) {
+                log.warn("Prefix $prefix does not have required data for us to continue.")
+            } else if (!api.hasDefinition(definitionName, definitionVersion)) {
+                log.warn("K8s RB Definition ($definitionName/$definitionVersion) not found ")
+            } else if (templateName == "") {
+                log.warn("K8s rb template name is empty! Either define template name to use or choose default")
+            } else if (api.hasTemplate(definitionName, definitionVersion, templateName)) {
+                log.info("Template already existing - skipping upload")
+            } else {
+                log.info("Uploading K8s template..")
+                outputPrefixStatuses[prefix] = OUTPUT_ERROR
+                var templateSource: String? = prefixInputParamsMap[INPUT_K8S_TEMPLATE_SOURCE]?.returnNullIfMissing()?.asText()
+                if (templateSource == null) {
+                    templateSource = templateName
+                    log.info("Template name used instead of template source")
+                }
+                val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
+                val artifact: ArtifactDefinition = bluePrintContext.nodeTemplateArtifact(nodeTemplateName, templateSource)
+                if (artifact.type != BlueprintConstants.MODEL_TYPE_ARTIFACT_K8S_PROFILE)
+                    throw BlueprintProcessorException(
+                        "Unexpected template artifact type for template source $templateSource. Expecting: $artifact.type"
+                    )
+                val template = K8sTemplate()
+                template.templateName = templateName
+                template.description = templateSource
+
+                val templateFilePath: Path = prepareTemplateFile(templateName, templateSource, artifact.file)
+                api.createTemplate(definitionName, definitionVersion, template)
+                api.uploadTemplate(definitionName, definitionVersion, template, templateFilePath)
+
+                log.info("K8s Profile Upload Completed")
+                outputPrefixStatuses[prefix] = OUTPUT_UPLOADED
+            }
+        }
+        bluePrintRuntimeService.setNodeTemplateAttributeValue(
+            nodeTemplateName,
+            OUTPUT_STATUSES,
+            outputPrefixStatuses.asJsonNode()
+        )
+    }
+
+    override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
+        bluePrintRuntimeService.getBlueprintError().addError(runtimeException.message!!)
+    }
+
+    private fun getTemplatePrefixList(node: JsonNode?): ArrayList<String> {
+        val result = ArrayList<String>()
+        when (node) {
+            is ArrayNode -> {
+                val arrayNode = node.toList()
+                for (prefixNode in arrayNode)
+                    result.add(prefixNode.asText())
+            }
+            is ObjectNode -> {
+                result.add(node.asText())
+            }
+        }
+        return result
+    }
+
+    private suspend fun prepareTemplateFile(k8sRbTemplateName: String, ks8ProfileSource: String, ks8ProfileLocation: String): Path {
+        val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
+        val bluePrintBasePath: String = bluePrintContext.rootPath
+        val profileSourceFileFolderPath: Path = Paths.get(
+            bluePrintBasePath.plus(File.separator).plus(ks8ProfileLocation)
+        )
+
+        if (profileSourceFileFolderPath.toFile().exists() && !profileSourceFileFolderPath.toFile().isDirectory)
+            return profileSourceFileFolderPath
+        else if (profileSourceFileFolderPath.toFile().exists()) {
+            log.info("Profile building started from source $ks8ProfileSource")
+            val properties: MutableMap<String, Any> = mutableMapOf()
+            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] = false
+            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_KEY] = ""
+            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_ID] = ""
+            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOURCE_TYPE] = ""
+            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_OCCURRENCE] = 1
+            properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_RESOLUTION_SUMMARY] = false
+            val resolutionResult: Pair<String, MutableList<ResourceAssignment>> = resourceResolutionService.resolveResources(
+                bluePrintRuntimeService,
+                nodeTemplateName,
+                ks8ProfileSource,
+                properties
+            )
+            val tempMainPath: File = createTempDir("k8s-profile-", "")
+            val tempProfilePath: File = createTempDir("content-", "", tempMainPath)
+
+            val resolvedJsonContent = resolutionResult.second
+                .associateBy({ it.name }, { it.property?.value })
+                .asJsonNode()
+
+            try {
+                templateLocation(profileSourceFileFolderPath.toFile(), resolvedJsonContent, tempProfilePath)
+                // Preparation of the final profile content
+                val finalTemplateFilePath = Paths.get(
+                    tempMainPath.toString().plus(File.separator).plus(
+                        "$k8sRbTemplateName.tar.gz"
+                    )
+                )
+                if (!BlueprintArchiveUtils.compress(tempProfilePath, finalTemplateFilePath.toFile(), ArchiveType.TarGz)) {
+                    throw BlueprintProcessorException("Profile compression has failed")
+                }
+                FileUtils.deleteDirectory(tempProfilePath)
+
+                return finalTemplateFilePath
+            } catch (t: Throwable) {
+                FileUtils.deleteDirectory(tempMainPath)
+                throw t
+            }
+        } else
+            throw BlueprintProcessorException("Profile source $ks8ProfileLocation is missing in CBA folder")
+    }
+
+    private fun templateLocation(location: File, params: JsonNode, destinationFolder: File) {
+        val directoryListing: Array<File>? = location.listFiles()
+        if (directoryListing != null) {
+            for (child in directoryListing) {
+                var newDestinationFolder = destinationFolder.toPath()
+                if (child.isDirectory)
+                    newDestinationFolder = Paths.get(destinationFolder.toString().plus(File.separator).plus(child.name))
+
+                templateLocation(child, params, newDestinationFolder.toFile())
+            }
+        } else if (!location.isDirectory) {
+            if (location.extension.toLowerCase() == "vtl") {
+                templateFile(location, params, destinationFolder)
+            }
+        }
+    }
+
+    private fun templateFile(templateFile: File, params: JsonNode, destinationFolder: File) {
+        val finalFile = File(destinationFolder.path.plus(File.separator).plus(templateFile.nameWithoutExtension))
+        val fileContent = templateFile.bufferedReader().readText()
+        val finalFileContent = BlueprintVelocityTemplateService.generateContent(
+            fileContent,
+            params, true
+        )
+        if (!destinationFolder.exists())
+            Files.createDirectories(destinationFolder.toPath())
+        finalFile.bufferedWriter().use { out -> out.write(finalFileContent) }
+    }
+}
diff --git a/ms/blueprintsprocessor/functions/k8s-connection-plugin/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/definition/template/K8sTemplate.kt b/ms/blueprintsprocessor/functions/k8s-connection-plugin/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/k8s/definition/template/K8sTemplate.kt
new file mode 100644 (file)
index 0000000..e2c1270
--- /dev/null
@@ -0,0 +1,26 @@
+package org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.definition.template
+
+import com.fasterxml.jackson.annotation.JsonProperty
+
+class K8sTemplate {
+
+    @get:JsonProperty("template-name")
+    var templateName: String? = null
+
+    @get:JsonProperty("description")
+    var description: String? = null
+
+    override fun toString(): String {
+        return "$templateName:$description"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return javaClass.hashCode()
+    }
+}