From 532cfff8eaad73781cbb410064cd3b4bda5e3095 Mon Sep 17 00:00:00 2001 From: "j.blixt" Date: Wed, 31 Mar 2021 11:48:49 +0200 Subject: [PATCH] [vFW_CNF_CDS] Add workflow health-check and K8sHealthCheck.kt script MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Issue-ID: INT-1899 Signed-off-by: j.blixt Signed-off-by: Grzegorz Wielgosinski Signed-off-by: Konrad Bańka Change-Id: I7d52c49bd0e40d30a560b2012362d38488392be6 --- heat/vFW_CNF_CDS/templates/cba-dd.json | 27 +++++ .../templates/cba/Definitions/artifact_types.json | 3 +- .../templates/cba/Definitions/data_types.json | 65 +++++++++++- .../templates/cba/Definitions/node_types.json | 79 +------------- .../templates/cba/Definitions/vFW_CNF_CDS.json | 107 +++++++++++++++++++ .../templates/cba/Scripts/kotlin/K8sHealthCheck.kt | 113 +++++++++++++++++++++ .../cba/Scripts/kotlin/SimpleStatusCheck.kt | 15 ++- heat/vFW_CNF_CDS/templates/cba/pom.xml | 35 +++---- 8 files changed, 341 insertions(+), 103 deletions(-) create mode 100644 heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/K8sHealthCheck.kt diff --git a/heat/vFW_CNF_CDS/templates/cba-dd.json b/heat/vFW_CNF_CDS/templates/cba-dd.json index f4ba0829..157d8b38 100644 --- a/heat/vFW_CNF_CDS/templates/cba-dd.json +++ b/heat/vFW_CNF_CDS/templates/cba-dd.json @@ -354,6 +354,33 @@ } } }, + { + "name": "k8s-rb-instance-release-name", + "tags": "k8s, cnf, profile, k8s-rb-instance-release-name", + "data_type": "string", + "description": "Name of the release for the helm package instance in k8s", + "entry_schema": "string", + "updatedBy": "Rajewski, Lukasz ", + "definition": { + "tags": "k8s, cnf, profile, k8s-rb-instance-release-name", + "name": "k8s-rb-instance-release-name", + "property": { + "description": "Name of the release for the helm package instance in k8s", + "type": "string" + }, + "group": "default", + "updated-by": "Rajewski, Lukasz ", + "sources": { + "input": { + "type": "source-input" + }, + "default": { + "type": "source-default", + "properties": {} + } + } + } + }, { "name": "k8s-rb-profile-namespace", "tags": "k8s, cnf, profile, namespace, k8s-rb-profile-namespace", diff --git a/heat/vFW_CNF_CDS/templates/cba/Definitions/artifact_types.json b/heat/vFW_CNF_CDS/templates/cba/Definitions/artifact_types.json index 778394d7..70935b3b 100644 --- a/heat/vFW_CNF_CDS/templates/cba/Definitions/artifact_types.json +++ b/heat/vFW_CNF_CDS/templates/cba/Definitions/artifact_types.json @@ -23,8 +23,7 @@ "description": " Velocity Template used for Configuration", "file_ext": [ "vtl" - ], - "version": "1.0.0" + ] } } } diff --git a/heat/vFW_CNF_CDS/templates/cba/Definitions/data_types.json b/heat/vFW_CNF_CDS/templates/cba/Definitions/data_types.json index f4d9266d..e797e79b 100644 --- a/heat/vFW_CNF_CDS/templates/cba/Definitions/data_types.json +++ b/heat/vFW_CNF_CDS/templates/cba/Definitions/data_types.json @@ -5,16 +5,32 @@ "description": "Dynamic DataType definition for workflow(config-assign).", "properties": { "config-deploy-setup": { + "description": "configuration for config value setup", "type": "json" }, "service-instance-id": { + "constraints": [ + {} + ], "description": "", + "entry_schema": { + "type": "" + }, "required": false, + "status": "", "type": "string" }, "vf-modules-list": { "type": "json" }, + "vf-modules-list-aai": { + "description": "list of modules associated with vnf from AAI", + "type": "json" + }, + "vf-modules-list-sdnc": { + "description": "list of modules associated with vnf from MDSAL", + "type": "json" + }, "vnf-id": { "description": "", "required": false, @@ -28,14 +44,61 @@ "description": "Dynamic DataType definition for workflow(config-deploy).", "properties": { "config-deploy-setup": { + "description": "configuration for config value setup", "type": "json" }, "service-instance-id": { + "constraints": [ + {} + ], "description": "", + "entry_schema": { + "type": "" + }, "required": false, + "status": "", "type": "string" }, - "vf-modules-list": { + "vf-modules-list-aai": { + "description": "list of modules associated with vnf from AAI", + "type": "json" + }, + "vf-modules-list-sdnc": { + "description": "list of modules associated with vnf from MDSAL", + "type": "json" + }, + "vnf-id": { + "description": "", + "required": false, + "type": "string" + } + } + }, + "dt-health-check-properties": { + "description": "Dynamic DataType definition for workflow(health-check).", + "properties": { + "config-deploy-setup": { + "description": "configuration for config value setup", + "type": "json" + }, + "service-instance-id": { + "constraints": [ + {} + ], + "description": "", + "entry_schema": { + "type": "" + }, + "required": false, + "status": "", + "type": "string" + }, + "vf-modules-list-aai": { + "description": "list of modules associated with vnf from AAI", + "type": "json" + }, + "vf-modules-list-sdnc": { + "description": "list of modules associated with vnf from MDSAL", "type": "json" }, "vnf-id": { diff --git a/heat/vFW_CNF_CDS/templates/cba/Definitions/node_types.json b/heat/vFW_CNF_CDS/templates/cba/Definitions/node_types.json index 98ed5d2a..5ad44764 100644 --- a/heat/vFW_CNF_CDS/templates/cba/Definitions/node_types.json +++ b/heat/vFW_CNF_CDS/templates/cba/Definitions/node_types.json @@ -260,7 +260,7 @@ "dynamic-properties": { "description": "Dynamic Json Content or DSL Json reference.", "required": false, - "type": "json" + "type": "boolean" }, "occurrence": { "default": 1, @@ -594,83 +594,6 @@ "derived_from": "tosca.nodes.Root", "description": "TOSCA base type for Resource Sources", "version": "1.0.0" - }, - "tosca.nodes.Vnf": { - "derived_from": "tosca.nodes.Root", - "description": "This is VNF Node Type", - "version": "1.0.0" - }, - "tosca.nodes.Workflow": { - "derived_from": "tosca.nodes.Root", - "description": "This is Directed Graph Node Type", - "version": "1.0.0" - }, - "vnf-netconf-device": { - "capabilities": { - "netconf": { - "properties": { - "connection-time-out": { - "default": 30, - "required": false, - "type": "integer" - }, - "login-account": { - "default": "sdnc-tacacs", - "required": true, - "type": "string" - }, - "login-key": { - "default": "sdnc", - "required": true, - "type": "string" - }, - "port-number": { - "default": 830, - "required": true, - "type": "integer" - }, - "source": { - "default": "npm", - "required": false, - "type": "string" - }, - "target-ip-address": { - "required": true, - "type": "string" - } - }, - "type": "tosca.capabilities.Netconf" - }, - "restconf": { - "properties": { - "connection-time-out": { - "default": 30, - "required": false, - "type": "integer" - }, - "login-account": { - "required": true, - "type": "string" - }, - "login-key": { - "required": true, - "type": "string" - }, - "port-number": { - "required": true, - "type": "integer" - }, - "target-ip-address": { - "required": true, - "type": "string" - } - }, - "type": "tosca.capabilities.Restconf" - } - }, - "derived_from": "tosca.nodes.Vnf", - "description": "This is VNF Device with Netconf Capability", - "version": "1.0.0" } } } diff --git a/heat/vFW_CNF_CDS/templates/cba/Definitions/vFW_CNF_CDS.json b/heat/vFW_CNF_CDS/templates/cba/Definitions/vFW_CNF_CDS.json index c17caca2..22f5749b 100644 --- a/heat/vFW_CNF_CDS/templates/cba/Definitions/vFW_CNF_CDS.json +++ b/heat/vFW_CNF_CDS/templates/cba/Definitions/vFW_CNF_CDS.json @@ -224,6 +224,94 @@ "type": "dt-config-deploy-properties" } } + }, + "health-check": { + "steps": { + "config-setup": { + "description": "Gather necessary input for config init and status verification", + "target": "config-setup-process", + "activities": [ + { + "call_operation": "ResourceResolutionComponent.process" + } + ], + "on_success": [ + "config-apply" + ], + "on_failure": [ + "handle_error" + ] + }, + "config-apply": { + "description": "Activate K8s config template", + "target": "k8s-config-apply", + "activities": [ + { + "call_operation": "K8sConfigTemplateComponent.process" + } + ], + "on_success": [ + "status-verification-script" + ] + }, + "status-verification-script": { + "description": "Simple status verification script", + "target": "simple-status-check", + "activities": [ + { + "call_operation": "ComponentScriptExecutor.process" + } + ], + "on_success": [ + "health-check-process" + ], + "on_failure": [ + "handle_error" + ] + }, + "health-check-process": { + "description": "Start health check script", + "target": "health-check-script", + "activities": [ + { + "call_operation": "ComponentScriptExecutor.process" + } + ], + "on_success": [ + "collect-results" + ], + "on_failure": [ + "handle_error" + ] + }, + "handle_error": { + "description": "Simple error verification script", + "target": "simple-error-check", + "activities": [ + { + "call_operation": "ComponentScriptExecutor.process" + } + ], + "on_success": [ + "collect-results" + ] + }, + "collect-results": { + "description": "Final collection of results", + "target": "collect-results" + } + }, + "inputs": { + "resolution-key": { + "required": true, + "type": "string" + }, + "config-deploy-properties": { + "description": "Dynamic PropertyDefinition for workflow(config-deploy).", + "required": true, + "type": "dt-config-deploy-properties" + } + } } }, "node_templates": { @@ -438,6 +526,25 @@ } } }, + "health-check-script": { + "type": "component-script-executor", + "interfaces": { + "ComponentScriptExecutor": { + "operations": { + "process": { + "inputs": { + "script-type": "kotlin", + "script-class-reference": "org.onap.ccsdk.cds.blueprintsprocessor.services.execution.scripts.K8sHealthCheck", + "instance-dependencies": [ + "blueprintPropertiesService" + ], + "dynamic-properties": "*simple-status-properties" + } + } + } + } + } + }, "config-setup-process": { "type": "component-resource-resolution", "interfaces": { diff --git a/heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/K8sHealthCheck.kt b/heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/K8sHealthCheck.kt new file mode 100644 index 00000000..6c45b4e9 --- /dev/null +++ b/heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/K8sHealthCheck.kt @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2021 Samsung Electronics + * 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.services.execution.scripts + +import com.fasterxml.jackson.databind.node.ObjectNode +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +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.instance.K8sPluginInstanceApi +import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.instance.healthcheck.K8sRbInstanceHealthCheck +import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.instance.healthcheck.K8sRbInstanceHealthCheckSimple +import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction +import org.slf4j.LoggerFactory + +open class K8sHealthCheck : AbstractScriptComponentFunction() { + + private val log = LoggerFactory.getLogger(K8sHealthCheck::class.java)!! + + override fun getName(): String { + return "K8sHealthCheck" + } + + private fun initPluginApi(): K8sPluginInstanceApi { + val bluePrintPropertiesService: BlueprintPropertiesService = this.functionDependencyInstanceAsType("blueprintPropertiesService")!! + val k8sConfiguration = K8sConnectionPluginConfiguration(bluePrintPropertiesService) + + return K8sPluginInstanceApi(k8sConfiguration) + } + + override suspend fun processNB(executionRequest: ExecutionServiceInput) { + val instanceApi = initPluginApi() + + log.info("Health check script execution - START") + val configValueSetup: ObjectNode = getDynamicProperties("config-deploy-setup") as ObjectNode + log.info("Config Value Setup: $configValueSetup") + + val instanceHealthCheckList = startInstanceHealthCheck(configValueSetup, instanceApi) + val statuses = getStatuses(instanceHealthCheckList, instanceApi) + log.info("Health check script execution - END") + } + + private fun startInstanceHealthCheck(configValueSetup: ObjectNode, instanceApi: K8sPluginInstanceApi): List { + val healthCheckInstanceList = arrayListOf() + + configValueSetup.fields().forEach { + val instanceName = it.value.get("k8s-instance-id").asText() + val response: K8sRbInstanceHealthCheckSimple? = instanceApi.startInstanceHealthCheck(instanceName) + log.debug("K8sRbInstanceHealthCheckSimple response: $$response") + healthCheckInstanceList.add(HealthCheckInstance(instanceName, response?.id)) + } + log.info("healthCheckInstanceList: $healthCheckInstanceList") + + return healthCheckInstanceList + } + + private fun getStatuses(instanceHealthCheckList: List, instanceApi: K8sPluginInstanceApi): Map { + val statuses = hashMapOf() + runBlocking { + val jobs: List = instanceHealthCheckList.map { + launch { + log.info("Thread started: ${Thread.currentThread().name} for $it") + // WAIT APPROX 5 MINUTES + repeat(30) { _ -> + val response: K8sRbInstanceHealthCheck = instanceApi.getInstanceHealthCheck(it.heatStackId, it.healthCheckInstance!!)!! + log.debug("Response for $it: $response") + val status = response.status!! + if (!"RUNNING".equals(status, true)) { + statuses[it.heatStackId] = status + log.info("Poll status: $status for $it") + instanceApi.deleteInstanceHealthCheck(it.heatStackId, it.healthCheckInstance) + cancel() + } + delay(10_000L) + } + statuses[it.heatStackId] = "Timeout" + log.warn("Send delete hc request") + instanceApi.deleteInstanceHealthCheck(it.heatStackId, it.healthCheckInstance!!) + } + } + jobs.joinAll() + } + log.info("Get statuses finished:") + log.info("$statuses") + return statuses + } + + data class HealthCheckInstance(val heatStackId: String, val healthCheckInstance: String?) { + override fun toString(): String { + return "HealthCheckInstance(heatStackId='$heatStackId', healthCheckInstance='$healthCheckInstance')" + } + } + + override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) { + log.info("Executing Recovery") + bluePrintRuntimeService.getBlueprintError().addError("${runtimeException.message}", getName()) + } +} diff --git a/heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/SimpleStatusCheck.kt b/heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/SimpleStatusCheck.kt index 90330644..e2d10534 100644 --- a/heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/SimpleStatusCheck.kt +++ b/heat/vFW_CNF_CDS/templates/cba/Scripts/kotlin/SimpleStatusCheck.kt @@ -40,7 +40,7 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() { val configValueSetup: ObjectNode = getDynamicProperties("config-deploy-setup") as ObjectNode val bluePrintPropertiesService: BlueprintPropertiesService = - this.functionDependencyInstanceAsType("blueprintPropertiesService") + this.functionDependencyInstanceAsType("blueprintPropertiesService") val k8sConfiguration = K8sConnectionPluginConfiguration(bluePrintPropertiesService) @@ -54,7 +54,10 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() { val instanceName = it.value.get("k8s-instance-id").asText() val instanceStatus: K8sRbInstanceStatus? = instanceApi.getInstanceStatus(instanceName) + log.debug("Get status for $instanceName") + var status = "" instanceStatus?.resourcesStatus?.forEach { + log.debug("Resource: name=$it.name kind=$it.gvk.kind group=$it.gvk.group version=$it.gvk.version") if (it.gvk?.kind == "Pod") { var version = it.gvk?.version!! if (it.gvk?.group!! != "") @@ -62,10 +65,12 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() { // val podStatus = instanceApi.queryInstanceStatus(instanceName, it.gvk?.kind!!, version, it.name, null) // log.info(podStatus.toString()) val podState = it.status?.get("status") as Map - - if ((podState["phase"] as String) != "Running") { + status = podState["phase"] as String + if (status != "Running") { continueCheck = true - log.info("Pod ${it.name} [$vfModuleName] has invalid state ${(podState["phase"])}") + log.info("Pod ${it.name} [$vfModuleName] has INVALID state ${(podState["phase"])}") + } else { + log.info("Pod ${it.name} [$vfModuleName] has VALID state ${(podState["phase"])}") } } } @@ -79,7 +84,7 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() { checkCount = 0 } - log.info("SIMPLE STATUS CHECK - END") + log.info("SIMPLE STATUS CHECK - END SUCCESS") } override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) { diff --git a/heat/vFW_CNF_CDS/templates/cba/pom.xml b/heat/vFW_CNF_CDS/templates/cba/pom.xml index fdb149ee..f03f4dc1 100644 --- a/heat/vFW_CNF_CDS/templates/cba/pom.xml +++ b/heat/vFW_CNF_CDS/templates/cba/pom.xml @@ -17,29 +17,15 @@ 4.0.0 - - - org.onap.ccsdk.cds.blueprintsprocessor.modules - processor-core - 1.1.1-SNAPSHOT - compile - - - org.onap.ccsdk.cds.blueprintsprocessor.functions - k8s-connection-plugin - 1.1.1-SNAPSHOT - compile - - - org.onap.ccsdk.cds.blueprintsprocessor + org.onap.ccsdk.cds.components.cba test-blueprint-kotlin-parent - 1.0.0-SNAPSHOT + 1.2.0-SNAPSHOT vFW_CNF_CDS - 1.0.0-SNAPSHOT + 1.2.0-SNAPSHOT pom @@ -54,4 +40,19 @@ api/v1/blueprint-model/publish + + + + org.onap.ccsdk.cds.blueprintsprocessor.modules + processor-core + 1.1.2-SNAPSHOT + compile + + + org.onap.ccsdk.cds.blueprintsprocessor.functions + k8s-connection-plugin + 1.1.2-SNAPSHOT + compile + + -- 2.16.6