From 5762244a0c6282557169c8183e464f6950438122 Mon Sep 17 00:00:00 2001 From: Lukasz Rajewski Date: Fri, 21 Oct 2022 17:36:17 +0200 Subject: [PATCH] [APACHECNF] Update healthcheck and add scale workflow Issue-ID: INT-2164 Signed-off-by: Lukasz Rajewski Change-Id: I7cf59f58592321ec06d8d812bd081293130ec74d --- tutorials/ApacheCNF/automation/README.md | 7 +- tutorials/ApacheCNF/automation/healthcheck.py | 13 ++- tutorials/ApacheCNF/automation/scale.py | 100 +++++++++++++++++++++ tutorials/ApacheCNF/service_config.yaml | 2 +- .../ApacheCNF/templates/cba/Definitions/CNF.json | 15 ++-- .../templates/cba/Definitions/data_types.json | 35 ++++++++ .../Definitions/resources_definition_types.json | 19 ++++ .../cba/Scripts/kotlin/ConfigDeploySetup.kt | 19 ++-- .../templates/cba/Scripts/kotlin/K8sHealthCheck.kt | 3 +- .../cba/Scripts/kotlin/SimpleStatusCheck.kt | 3 +- .../cba/Templates/config-setup-mapping.json | 14 +++ 11 files changed, 207 insertions(+), 23 deletions(-) create mode 100644 tutorials/ApacheCNF/automation/scale.py diff --git a/tutorials/ApacheCNF/automation/README.md b/tutorials/ApacheCNF/automation/README.md index 5b2b80b3..66138c98 100644 --- a/tutorials/ApacheCNF/automation/README.md +++ b/tutorials/ApacheCNF/automation/README.md @@ -28,5 +28,8 @@ 8. Run script `python create_cloud_regions.py` in order to create **k8s or openstack cloud region** 9. Onboard CNF `python onboard.py` 10. Instantiate CNF `python instantiate.py` -11. Once test is done, CNF service instance can be deleted with `python delete.py` command - +11. To run healtcheck operation execute `python healthcheck.py ` where [int] (default 1) + indicates iteration number to run status check in case of failure +12. To run scale operation execute `python scale.py ` where [int] (default 1) + indicates the number of desired replicas of Apache pods +13. Once test is done, CNF service instance can be deleted with `python delete.py` command diff --git a/tutorials/ApacheCNF/automation/healthcheck.py b/tutorials/ApacheCNF/automation/healthcheck.py index 9c33375e..6fe72397 100644 --- a/tutorials/ApacheCNF/automation/healthcheck.py +++ b/tutorials/ApacheCNF/automation/healthcheck.py @@ -1,5 +1,6 @@ # ============LICENSE_START======================================================= # Copyright (C) 2021 Samsung +# Modification Copyright (C) 2022 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. @@ -16,6 +17,7 @@ # ============LICENSE_END========================================================= import logging import os +import sys import zipfile from io import BytesIO @@ -61,7 +63,7 @@ def resolve_hc_inputs(config: Config): vnf_id = vnfs[0].vnf_id return service_id, vnf_id -def main(): +def main(status_count): mypath = os.path.dirname(os.path.realpath(__file__)) config = Config(env_dict=VariablesDict.env_variable) for vnf in config.service_model["vnfs"]: @@ -77,7 +79,8 @@ def main(): cds_input = {"health-check-properties": { "service-instance-id": serv_id, - "vnf-id": vnf_id + "vnf-id": vnf_id, + "status-check-max-count": int(status_count) } } @@ -90,4 +93,8 @@ def main(): logger.info("Please check cds-blueprints-processor logs to see exact status") if __name__ == "__main__": - main() + status_count = 1 + if len(sys.argv) > 1: + status_count = sys.argv[1] + print(f"Status Check Max Count: %s" % status_count) + main(status_count) diff --git a/tutorials/ApacheCNF/automation/scale.py b/tutorials/ApacheCNF/automation/scale.py new file mode 100644 index 00000000..e4623f41 --- /dev/null +++ b/tutorials/ApacheCNF/automation/scale.py @@ -0,0 +1,100 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2022 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. +# +# ============LICENSE_END========================================================= +import logging +import os +import sys +import zipfile +from io import BytesIO + +from onapsdk.aai.business import Customer +from onapsdk.cds.blueprint import Workflow, Blueprint +from config import Config, VariablesDict + +#FIXME remove from global scope +logger = logging.getLogger("") +logger.setLevel(logging.INFO) +fh = logging.StreamHandler() +fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s') +fh.setFormatter(fh_formatter) +logger.addHandler(fh) + + +def resolve_inputs(config: Config): + logger.info("******** Check Customer *******") + customer_id = config.service_instance["customer_id"] + customer = Customer.get_by_global_customer_id(customer_id) + if customer is None: + raise Exception("Customer %s wasn't found in ONAP" % customer_id) + logger.info("******** Check Service Subscription *******") + service_subscription = None + for service_sub in customer.service_subscriptions: + logger.debug("Service subscription %s is found", service_sub.service_type) + if service_sub.service_type == config.service_model["model_name"]: + logger.info("Service %s subscribed", config.service_model["model_name"]) + service_subscription = service_sub + break + logger.info("******** Retrieve Service Metadata *******") + service_instance = None + for single_service in service_subscription.service_instances: + if single_service.instance_name == config.service_instance["instance_name"]: + service_instance = single_service + break + service_id = service_instance.instance_id + vnfs = list(service_instance.vnf_instances) + if len(vnfs) > 1: + raise NotImplementedError("Service %s is composed of more than one vnf!" % service_id) + if not vnfs: + raise Exception("Service %s doesn't contain any vnfs" % service_id) + vnf_id = vnfs[0].vnf_id + return service_id, vnf_id + +def main(replica_count): + mypath = os.path.dirname(os.path.realpath(__file__)) + config = Config(env_dict=VariablesDict.env_variable) + for vnf in config.service_model["vnfs"]: + file = vnf["vsp"]["vsp_file"] + file_path = os.path.join(mypath, file) + with zipfile.ZipFile(file_path, 'r') as package: + cba_io = BytesIO(package.read("CBA.zip")) + cba_io.seek(0) + blueprint = Blueprint(cba_io.read()) + + healthcheck: Workflow = blueprint.get_workflow_by_name('scale') + serv_id, vnf_id = resolve_inputs(config) + cds_input = {"scale-properties": + { + "service-instance-id": serv_id, + "vnf-id": vnf_id, + "status-check-max-count": 20, + "replica-count": int(replica_count) + } + } + + logger.info("Requesting Scale for CBA %s:%s with inputs:\n%s", + blueprint.metadata.template_name, + blueprint.metadata.template_version, + cds_input) + result = healthcheck.execute(cds_input) + logger.info("Scale process completed with result: %s", result) + logger.info("Please check cds-blueprints-processor logs to see exact status") + +if __name__ == "__main__": + replica_count = 1 + if len(sys.argv) > 1: + replica_count = sys.argv[1] + print(f"Replica Count: %s" % replica_count) + main(replica_count) diff --git a/tutorials/ApacheCNF/service_config.yaml b/tutorials/ApacheCNF/service_config.yaml index 623832a5..47bab15f 100644 --- a/tutorials/ApacheCNF/service_config.yaml +++ b/tutorials/ApacheCNF/service_config.yaml @@ -90,6 +90,6 @@ service_instance: k8s-rb-profile-namespace: "{{ user_params.k8s_namespace }}" k8s-rb-config-template-name: replica-count-template k8s-rb-config-name: replica-count-change - k8s-rb-config-value-source: custom-values + k8s-rb-config-value-source: default-values # pnfs: # - model_name: pnf_example diff --git a/tutorials/ApacheCNF/templates/cba/Definitions/CNF.json b/tutorials/ApacheCNF/templates/cba/Definitions/CNF.json index 2e30ce01..4f44e8a1 100644 --- a/tutorials/ApacheCNF/templates/cba/Definitions/CNF.json +++ b/tutorials/ApacheCNF/templates/cba/Definitions/CNF.json @@ -56,6 +56,15 @@ "config-deploy", "config-deploy-setup" ] + }, + "status-check-max-count": { + "get_attribute": [ + "config-setup-process", + "", + "assignment-map", + "config-deploy", + "status-check-max-count" + ] } } }, @@ -453,9 +462,6 @@ ], "on_success": [ "health-check-process" - ], - "on_failure": [ - "handle_error" ] }, "health-check-process": { @@ -468,9 +474,6 @@ ], "on_success": [ "collect-results" - ], - "on_failure": [ - "handle_error" ] }, "handle_error": { diff --git a/tutorials/ApacheCNF/templates/cba/Definitions/data_types.json b/tutorials/ApacheCNF/templates/cba/Definitions/data_types.json index 603b0baa..1573c81c 100644 --- a/tutorials/ApacheCNF/templates/cba/Definitions/data_types.json +++ b/tutorials/ApacheCNF/templates/cba/Definitions/data_types.json @@ -30,6 +30,17 @@ "description": "", "required": false, "type": "string" + }, + "replica-count": { + "description": "replica count for apache pods", + "type": "integer", + "required": false + }, + "status-check-max-count": { + "description": "max status check count for pods", + "type": "integer", + "required": false, + "default": 30 } }, "derived_from": "tosca.datatypes.Dynamic" @@ -57,6 +68,12 @@ "description": "", "required": false, "type": "string" + }, + "status-check-max-count": { + "description": "max status check count for pods", + "type": "integer", + "required": false, + "default": 30 } }, "derived_from": "tosca.datatypes.Dynamic" @@ -89,6 +106,12 @@ "description": "replica count for apache pods", "type": "integer", "default": 2 + }, + "status-check-max-count": { + "description": "max status check count for pods", + "type": "integer", + "required": false, + "default": 30 } }, "derived_from": "tosca.datatypes.Dynamic" @@ -121,6 +144,12 @@ "description": "replica count for apache pods", "type": "integer", "default": 1 + }, + "status-check-max-count": { + "description": "max status check count for pods", + "type": "integer", + "required": false, + "default": 30 } }, "derived_from": "tosca.datatypes.Dynamic" @@ -158,6 +187,12 @@ "description": "the data content of the policy request parameters", "required": false, "type": "json" + }, + "status-check-max-count": { + "description": "max status check count for pods", + "type": "integer", + "required": false, + "default": 30 } }, "derived_from": "tosca.datatypes.Dynamic" diff --git a/tutorials/ApacheCNF/templates/cba/Definitions/resources_definition_types.json b/tutorials/ApacheCNF/templates/cba/Definitions/resources_definition_types.json index 38b5fee4..31f04c11 100644 --- a/tutorials/ApacheCNF/templates/cba/Definitions/resources_definition_types.json +++ b/tutorials/ApacheCNF/templates/cba/Definitions/resources_definition_types.json @@ -1439,6 +1439,25 @@ } } }, + "status-check-max-count": { + "tags": "max status check count for pods", + "name": "status-check-max-count", + "property": { + "description": "max status check count for pods", + "type": "integer" + }, + "group": "default", + "updated-by": "Lukasz Rajewski ", + "sources": { + "input": { + "type": "source-input" + }, + "default": { + "type": "source-default", + "properties": {} + } + } + }, "service-instance-name": { "tags": "service-instance-name", "name": "service-instance-name", diff --git a/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/ConfigDeploySetup.kt b/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/ConfigDeploySetup.kt index 77d86d04..f925be06 100644 --- a/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/ConfigDeploySetup.kt +++ b/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/ConfigDeploySetup.kt @@ -70,17 +70,22 @@ open class ConfigDeploySetup() : ResourceAssignmentProcessor() { } } } else if (executionRequest.name == "replica-count") { - var value = raRuntimeService.getInputValue(executionRequest.name) retValue = "1" - if (!value.isNullOrMissing()) { - retValue = value.asText() - } else { - value = raRuntimeService.getInputValue("data") + try { + var value = raRuntimeService.getInputValue(executionRequest.name) if (!value.isNullOrMissing()) { - if (value["replicaCount"] != null) { - retValue = value["replicaCount"].asText() + retValue = value.asText() + } else { + value = raRuntimeService.getInputValue("data") + if (!value.isNullOrMissing()) { + if (value["replicaCount"] != null) { + retValue = value["replicaCount"].asText() + } } } + } catch (e: Exception) { + log.error(e.message, e) + log.info("Setting default replica count: 1") } } else if (executionRequest.name == "config-deploy-setup") { val modulesSdnc = raRuntimeService.getResolutionStore("vf-modules-list-sdnc")["vf-modules"] diff --git a/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/K8sHealthCheck.kt b/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/K8sHealthCheck.kt index dd87c6f5..30b4d96b 100644 --- a/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/K8sHealthCheck.kt +++ b/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/K8sHealthCheck.kt @@ -119,7 +119,6 @@ open class K8sHealthCheck : AbstractScriptComponentFunction() { } override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) { - log.info("Executing Recovery") - bluePrintRuntimeService.getBluePrintError().addError("${runtimeException.message}", getName()) + this.addError("${runtimeException.message}") } } diff --git a/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/SimpleStatusCheck.kt b/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/SimpleStatusCheck.kt index c99dcd4f..c1f59d98 100644 --- a/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/SimpleStatusCheck.kt +++ b/tutorials/ApacheCNF/templates/cba/Scripts/kotlin/SimpleStatusCheck.kt @@ -38,6 +38,7 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() { log.info("SIMPLE STATUS CHECK - START") val configValueSetup: ObjectNode = getDynamicProperties("config-deploy-setup") as ObjectNode + var checkCount: Int = getDynamicProperties("status-check-max-count").asInt() val bluePrintPropertiesService: BluePrintPropertiesService = this.functionDependencyInstanceAsType("bluePrintPropertiesService") @@ -46,7 +47,6 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() { val instanceApi = K8sPluginInstanceApi(k8sConfiguration) - var checkCount: Int = 30 // in the future to be read in from the input while (checkCount > 0) { var continueCheck = false configValueSetup.fields().forEach { it -> @@ -75,7 +75,6 @@ open class SimpleStatusCheck : AbstractScriptComponentFunction() { } override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) { - log.info("Executing Recovery") this.addError("${runtimeException.message}") } } diff --git a/tutorials/ApacheCNF/templates/cba/Templates/config-setup-mapping.json b/tutorials/ApacheCNF/templates/cba/Templates/config-setup-mapping.json index 95d2681f..34189bca 100644 --- a/tutorials/ApacheCNF/templates/cba/Templates/config-setup-mapping.json +++ b/tutorials/ApacheCNF/templates/cba/Templates/config-setup-mapping.json @@ -101,5 +101,19 @@ "dependencies": [ "service-instance-id" ] + }, + { + "name": "status-check-max-count", + "property": { + "description": "max status check count for pods", + "required": false, + "type": "integer", + "default": 30 + }, + "input-param": false, + "dictionary-name": "status-check-max-count", + "dictionary-source": "default", + "dependencies": [], + "version": 0 } ] -- 2.16.6