From: gummar Date: Tue, 18 Feb 2020 18:54:44 +0000 (+0000) Subject: Merge SW Upgrade Blueprint into PNF_AAI and create one UAT BP for PNF X-Git-Tag: 0.7.0~30 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=94ad509756f17e79c278e3cc2f87440009125cd1;p=ccsdk%2Fcds.git Merge SW Upgrade Blueprint into PNF_AAI and create one UAT BP for PNF UAT: Add support to multiple responses for a single request Set property IN_UAT=1 during UAT execution so blueprints can tune their settings to values more suitable for testing (like timeouts) Add 'times' field to specify expected number of invocations Add UAT blueprint script for PNF SW Upgrade UC Add current thread check for Hazelcast distributed lock Resolve URI before returning Issue-ID: CCSDK-2091 Change-Id: Id256bad043488f88f1b60015ebf9ade4be607fa2 Signed-off-by: gummar --- diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/PNF_CDS_RESTCONF.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/PNF_CDS_RESTCONF.json new file mode 100644 index 000000000..2e6c2f547 --- /dev/null +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/PNF_CDS_RESTCONF.json @@ -0,0 +1,424 @@ +{ + "tosca_definitions_version" : "controller_blueprint_1_0_0", + "metadata" : { + "template_author" : "Raj Gumma", + "author-email" : "raj.gumma@est.tech", + "user-groups" : "ADMIN, OPERATION", + "template_name" : "PNF_CDS_RESTCONF", + "template_version" : "1.0.0", + "template_tags" : "PNF, Restconf, config, configuration, software upgrade" + }, + "imports" : [ { + "file" : "Definitions/data_types.json" + }, { + "file" : "Definitions/relationship_types.json" + }, { + "file" : "Definitions/artifact_types.json" + }, { + "file" : "Definitions/node_types.json" + }, { + "file" : "Definitions/policy_types.json" + } ], + "dsl_definitions" : { + "config-deploy-properties" : { + "resolution-key" : { + "get_input" : "resolution-key" + } + }, + "precheck-properties" : { + "resolution-key" : { + "get_input" : "resolution-key" + } + }, + "downloadNeSw-properties" : { + "resolution-key" : { + "get_input" : "resolution-key" + } + }, + "activateNeSw-properties" : { + "resolution-key" : { + "get_input" : "resolution-key" + } + }, + "postcheck-properties" : { + "resolution-key" : { + "get_input" : "resolution-key" + } + } + }, + "topology_template" : { + "workflows" : { + "config-assign" : { + "steps" : { + "activate-process" : { + "description" : "Create a configlet", + "target" : "config-assign", + "activities" : [ { + "call_operation" : "" + } ] + } + }, + "inputs" : { + "resolution-key" : { + "required" : true, + "type" : "string" + }, + "store-result" : { + "required" : true, + "type" : "boolean" + }, + "config-assign-properties" : { + "description" : "Dynamic PropertyDefinition for workflow(config-assign).", + "required" : true, + "type" : "dt-config-assign-properties" + } + } + }, + "config-deploy" : { + "steps" : { + "activate-process" : { + "description" : "Send a configlet to the pnf", + "target" : "config-deploy", + "activities" : [ { + "call_operation" : "" + } ] + } + }, + "inputs" : { + "resolution-key" : { + "required" : true, + "type" : "string" + }, + "config-deploy-properties" : { + "description" : "Dynamic PropertyDefinition for workflow(config-deploy).", + "required" : true, + "type" : "dt-config-deploy-properties" + } + } + }, + "precheck" : { + "steps" : { + "activate-process" : { + "description" : "Check if pnf ready for sw upgrade", + "target" : "precheck", + "activities" : [ { + "call_operation" : "" + } ] + } + }, + "inputs" : { + "resolution-key" : { + "required" : true, + "type" : "string" + }, + "precheck-properties" : { + "description" : "Dynamic PropertyDefinition for precheck workflow(software-upgrade).", + "required" : true, + "type" : "dt-precheck-properties" + } + } + }, + "downloadNeSw" : { + "steps" : { + "activate-process" : { + "description" : "Trigger download new software for sw upgrade", + "target" : "downloadNeSw", + "activities" : [ { + "call_operation" : "" + } ] + } + }, + "inputs" : { + "resolution-key" : { + "required" : true, + "type" : "string" + }, + "downloadNeSw-properties" : { + "description" : "Dynamic PropertyDefinition for downloadNeSw workflow(software-upgrade).", + "required" : true, + "type" : "dt-downloadNeSw-properties" + } + } + }, + "activateNeSw" : { + "steps" : { + "activate-process" : { + "description" : "Trigger activation of target software version for pnf upgrade", + "target" : "activateNeSw", + "activities" : [ { + "call_operation" : "" + } ] + } + }, + "inputs" : { + "resolution-key" : { + "required" : true, + "type" : "string" + }, + "activateNeSw-properties" : { + "description" : "Dynamic PropertyDefinition for activateNeSw workflow(software-upgrade).", + "required" : true, + "type" : "dt-activateNeSw-properties" + } + } + }, + "postcheck" : { + "steps" : { + "activate-process" : { + "description" : "Check if pnf upgrade is completed", + "target" : "postcheck", + "activities" : [ { + "call_operation" : "" + } ] + } + }, + "inputs" : { + "resolution-key" : { + "required" : true, + "type" : "string" + }, + "postcheck-properties" : { + "description" : "Dynamic PropertyDefinition for postcheck workflow(software-upgrade).", + "required" : true, + "type" : "dt-postcheck-properties" + } + } + } + }, + "node_templates" : { + "config-assign" : { + "type" : "component-resource-resolution", + "interfaces" : { + "ResourceResolutionComponent" : { + "operations" : { + "process" : { + "inputs" : { + "resolution-key" : { + "get_input" : "resolution-key" + }, + "store-result" : true, + "artifact-prefix-names" : [ "config-assign" ] + }, + "outputs" : { + "resource-assignment-params" : { + "get_attribute" : [ "SELF", "assignment-params" ] + }, + "status" : "success" + } + } + } + } + }, + "artifacts" : { + "config-assign-template" : { + "type" : "artifact-template-velocity", + "file" : "Templates/config-assign-restconf-configlet-template.vtl" + }, + "config-assign-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/config-assign-pnf-mapping.json" + } + } + }, + "config-deploy" : { + "type" : "component-script-executor", + "interfaces" : { + "ComponentScriptExecutor" : { + "operations" : { + "process" : { + "implementation" : { + "primary" : "component-script", + "timeout" : 180, + "operation_host" : "SELF" + }, + "inputs" : { + "script-type" : "kotlin", + "script-class-reference" : "cba.pnf.config.aai.RestconfConfigDeploy", + "dynamic-properties" : "*config-deploy-properties" + }, + "outputs" : { + "response-data" : "", + "status" : "success" + } + } + } + } + }, + "artifacts" : { + "config-deploy-template" : { + "type" : "artifact-template-velocity", + "file" : "Templates/restconf-mount-template.vtl" + }, + "config-deploy-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/config-deploy-pnf-mapping.json" + } + } + }, + "precheck" : { + "type" : "component-script-executor", + "interfaces" : { + "ComponentScriptExecutor" : { + "operations" : { + "process" : { + "implementation" : { + "primary" : "component-script", + "timeout" : 180, + "operation_host" : "SELF" + }, + "inputs" : { + "script-type" : "kotlin", + "script-class-reference" : "cba.pnf.swug.RestconfSoftwareUpgrade", + "dynamic-properties" : "*precheck-properties" + }, + "outputs" : { + "response-data" : "", + "status" : "success" + } + } + } + } + }, + "artifacts" : { + "mount-node-template" : { + "type" : "artifact-template-velocity", + "file" : "Templates/restconf-mount-template.vtl" + }, + "mount-node-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/pnf-software-upgrade-mapping.json" + } + } + }, + "downloadNeSw" : { + "type" : "component-script-executor", + "interfaces" : { + "ComponentScriptExecutor" : { + "operations" : { + "process" : { + "implementation" : { + "primary" : "component-script", + "timeout" : 180, + "operation_host" : "SELF" + }, + "inputs" : { + "script-type" : "kotlin", + "script-class-reference" : "cba.pnf.swug.RestconfSoftwareUpgrade", + "dynamic-properties" : "*downloadNeSw-properties" + }, + "outputs" : { + "response-data" : "", + "status" : "success" + } + } + } + } + }, + "artifacts" : { + "mount-node-template" : { + "type" : "artifact-template-velocity", + "file" : "Templates/restconf-mount-template.vtl" + }, + "mount-node-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/pnf-software-upgrade-mapping.json" + }, + "configure-template" : { + "type" : "artifact-template-velocity", + "file" : "Templates/pnf-swug-config-template.vtl" + }, + "configure-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/pnf-software-upgrade-mapping.json" + }, + "download-ne-sw-template" : { + "type" : "artifact-template-velocity", + "file" : "Templates/pnf-swug-download-ne-sw-template.vtl" + }, + "download-ne-sw-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/pnf-software-upgrade-mapping.json" + } + } + }, + "activateNeSw" : { + "type" : "component-script-executor", + "interfaces" : { + "ComponentScriptExecutor" : { + "operations" : { + "process" : { + "implementation" : { + "primary" : "component-script", + "timeout" : 180, + "operation_host" : "SELF" + }, + "inputs" : { + "script-type" : "kotlin", + "script-class-reference" : "cba.pnf.swug.RestconfSoftwareUpgrade", + "dynamic-properties" : "*activateNeSw-properties" + }, + "outputs" : { + "response-data" : "", + "status" : "success" + } + } + } + } + }, + "artifacts" : { + "mount-node-template" : { + "type" : "artifact-template-velocity", + "file" : "Templates/restconf-mount-template.vtl" + }, + "mount-node-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/pnf-software-upgrade-mapping.json" + }, + "configure-template" : { + "type" : "artifact-template-velocity", + "file" : "Templates/pnf-swug-config-template.vtl" + }, + "configure-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/pnf-software-upgrade-mapping.json" + } + } + }, + "postcheck" : { + "type" : "component-script-executor", + "interfaces" : { + "ComponentScriptExecutor" : { + "operations" : { + "process" : { + "implementation" : { + "primary" : "component-script", + "timeout" : 180, + "operation_host" : "SELF" + }, + "inputs" : { + "script-type" : "kotlin", + "script-class-reference" : "cba.pnf.swug.RestconfSoftwareUpgrade", + "dynamic-properties" : "*postcheck-properties" + }, + "outputs" : { + "response-data" : "", + "status" : "success" + } + } + } + } + }, + "artifacts" : { + "mount-node-template" : { + "type" : "artifact-template-velocity", + "file" : "Templates/restconf-mount-template.vtl" + }, + "mount-node-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/pnf-software-upgrade-mapping.json" + } + } + } + } + } +} \ No newline at end of file diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/artifact_types.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/artifact_types.json similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/artifact_types.json rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/artifact_types.json diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/config-assign-pnf-mapping.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/config-assign-pnf-mapping.json similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/config-assign-pnf-mapping.json rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/config-assign-pnf-mapping.json diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/config-deploy-pnf-mapping.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/config-deploy-pnf-mapping.json similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/config-deploy-pnf-mapping.json rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/config-deploy-pnf-mapping.json diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/data_types.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/data_types.json new file mode 100644 index 000000000..e3d216c89 --- /dev/null +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/data_types.json @@ -0,0 +1,75 @@ +{ + "data_types" : { + "dt-config-assign-properties" : { + "description" : "Dynamic DataType definition for workflow(config-assign).", + "version" : "1.0.0", + "properties" : { }, + "derived_from" : "tosca.datatypes.Dynamic" + }, + "dt-config-deploy-properties" : { + "description" : "Dynamic DataType definition for workflow(config-deploy).", + "version" : "1.0.0", + "properties" : { + "pnf-ipv4-address" : { + "type" : "string" + }, + "pnf-id" : { + "type" : "string" + } + }, + "derived_from" : "tosca.datatypes.Dynamic" + }, + "dt-precheck-properties": { + "description": "Dynamic DataType definition for the precheck workflow(upgrade-software).", + "version": "1.0.0", + "properties": { + "pnf-id": { + "type": "string" + }, + "target-software-version": { + "type": "string" + } + }, + "derived_from": "tosca.datatypes.Dynamic" + }, + "dt-downloadNeSw-properties": { + "description": "Dynamic DataType definition for the downloadNeSw workflow(upgrade-software).", + "version": "1.0.0", + "properties": { + "pnf-id": { + "type": "string" + }, + "target-software-version": { + "type": "string" + } + }, + "derived_from": "tosca.datatypes.Dynamic" + }, + "dt-activateNeSw-properties": { + "description": "Dynamic DataType definition for the activateNeSw workflow(upgrade-software).", + "version": "1.0.0", + "properties": { + "pnf-id": { + "type": "string" + }, + "target-software-version": { + "type": "string" + } + }, + "derived_from": "tosca.datatypes.Dynamic" + }, + "dt-postcheck-properties": { + "description": "Dynamic DataType definition for the postcheck workflow(upgrade-software).", + "version": "1.0.0", + "properties": { + "pnf-id": { + "type": "string" + }, + "target-software-version": { + "type": "string" + } + }, + "derived_from": "tosca.datatypes.Dynamic" + } + } +} \ No newline at end of file diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/node_types.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/node_types.json similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/node_types.json rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/node_types.json diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/pnf-software-upgrade-mapping.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/pnf-software-upgrade-mapping.json new file mode 100644 index 000000000..2c3de2e49 --- /dev/null +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/pnf-software-upgrade-mapping.json @@ -0,0 +1,36 @@ +[ + { + "name": "pnf-id", + "input-param": true, + "property": { + "type": "string" + }, + "dictionary-name": "pnf-id", + "dictionary-source": "input", + "dependencies": [ + ] + }, + { + "name": "target-software-version", + "input-param": true, + "property": { + "type": "string" + }, + "dictionary-name": "target-software-version", + "dictionary-source": "input", + "dependencies": [ + ] + }, + { + "name": "pnf-ipv4-address", + "input-param": false, + "property": { + "type": "string" + }, + "dictionary-name": "pnf-ipaddress-aai", + "dictionary-source": "aai-data", + "dependencies": [ + "pnf-id" + ] + } +] diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/policy_types.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/policy_types.json similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/policy_types.json rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/policy_types.json diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/relationship_types.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/relationship_types.json similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/relationship_types.json rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/relationship_types.json diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/resources_definition_types.json b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/resources_definition_types.json similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/resources_definition_types.json rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Definitions/resources_definition_types.json diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Environments/source-db.properties b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Environments/source-db.properties similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Environments/source-db.properties rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Environments/source-db.properties diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Environments/source-rest.properties b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Environments/source-rest.properties similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Environments/source-rest.properties rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Environments/source-rest.properties diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Plans/CONFIG_configAssign.xml b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Plans/CONFIG_configAssign.xml similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Plans/CONFIG_configAssign.xml rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Plans/CONFIG_configAssign.xml diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Plans/CONFIG_configDeploy.xml b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Plans/CONFIG_configDeploy.xml similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Plans/CONFIG_configDeploy.xml rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Plans/CONFIG_configDeploy.xml diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Plans/CONFIG_upgradeSoftware.xml b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Plans/CONFIG_upgradeSoftware.xml new file mode 100644 index 000000000..52a9900b2 --- /dev/null +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Plans/CONFIG_upgradeSoftware.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Scripts/kotlin/RestconfConfigDeploy.kt b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfConfigDeploy.kt similarity index 96% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Scripts/kotlin/RestconfConfigDeploy.kt rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfConfigDeploy.kt index f0190e8ec..730565959 100644 --- a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Scripts/kotlin/RestconfConfigDeploy.kt +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfConfigDeploy.kt @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= -* Copyright (C) 2019 Nordix Foundation. +* Copyright (C) 2020 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,9 +67,9 @@ class RestconfConfigDeploy : AbstractScriptComponentFunction() { val jsonResult = mapper.readTree((result.body).toString()) - if(jsonResult.get("ietf-yang-patch:yang-patch-status").get("errors") != null) { + if (jsonResult.get("ietf-yang-patch:yang-patch-status").get("errors") != null) { log.error("There was an error configuring device") - } else { + } else { log.info("Device has been configured succesfully") } diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfSoftwareUpgrade.kt b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfSoftwareUpgrade.kt new file mode 100644 index 000000000..07e804b95 --- /dev/null +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfSoftwareUpgrade.kt @@ -0,0 +1,195 @@ +/* +* ============LICENSE_START======================================================= +* Copyright (C) 2020 Nordix Foundation. +* ================================================================================ +* 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========================================================= + */ + + +package cba.pnf.swug + +import com.fasterxml.jackson.databind.node.ObjectNode +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput +import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.contentFromResolvedArtifactNB +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.restconfApplyDeviceConfig +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.restconfDeviceConfig +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.restconfMountDevice +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.restconfUnMountDevice +import org.onap.ccsdk.cds.blueprintsprocessor.rest.restClientService +import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService +import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintRetryException +import org.onap.ccsdk.cds.controllerblueprints.core.logger +import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService +import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils + +class RestconfSoftwareUpgrade : AbstractScriptComponentFunction() { + + private val RESTCONF_SERVER_IDENTIFIER = "sdnc" + private val CONFIGLET_RESOURCE_PATH = "yang-ext:mount/pnf-sw-upgrade:software-upgrade" + private val log = logger(AbstractScriptComponentFunction::class.java) + private val TARGET_SOFTWARE_PATH = "$CONFIGLET_RESOURCE_PATH/upgrade-package/" + + override suspend fun processNB(executionRequest: ExecutionServiceInput) { + + // Extract request properties + val properties = requestPayloadActionProperty(executionRequest.actionIdentifiers.actionName + "-properties")!!.get(0) + val model= SoftwareUpgradeModel(getDynamicProperties("resolution-key").asText(), + BluePrintDependencyService.restClientService(RESTCONF_SERVER_IDENTIFIER), + properties.get("pnf-id").textValue(), properties.get("target-software-version").textValue(), + Action.getEnumFromActionName(executionRequest.actionIdentifiers.actionName)) + + log.info("Blueprint invoked for ${model.resolutionKey} for SW Upgrade : " + + "${model.action} for sw version ${model.targetSwVersion} on pnf: ${model.deviceId}") + + try { + val mountPayload = contentFromResolvedArtifactNB("mount-node") + log.debug("Mount Payload : $mountPayload") + restconfMountDevice(model.client, model.deviceId, mountPayload, mutableMapOf("Content-Type" to "application/json")) + + when (model.action) { + Action.PRE_CHECK -> processPrecheck(model) + Action.DOWNLOAD_NE_SW -> processDownloadNeSw(model) + Action.ACTIVATE_NE_SW -> processActivateNeSw(model) + Action.POST_CHECK -> processPostcheck(model) + Action.CANCEL -> processCancel(model) + } + + } catch (err: Exception) { + log.error("an error occurred while configuring device {}", err) + } finally { + restconfUnMountDevice(model.client, model.deviceId, "") + } + } + + private suspend fun processPrecheck(model: SoftwareUpgradeModel) { + log.debug("In PNF SW upgrade : processPreCheck") + //Log the current configuration for the subtree + val payloadObject = getCurrentConfig(model) + log.debug("Current sw version on pnf : ${payloadObject.get("software-upgrade")?.get("upgrade-package")?.get(0)?.get("software-version")?.asText()}") + log.info("PNF is Healthy!") + } + + private suspend fun processDownloadNeSw(model: SoftwareUpgradeModel) { + log.debug("In PNF SW upgrade : processDownloadNeSw") + //Check if there is existing config for the targeted software version + + var downloadConfigPayload: String + if (checkIfSwReadyToPerformAction(Action.PRE_CHECK, model)) { + downloadConfigPayload = contentFromResolvedArtifactNB("configure") + downloadConfigPayload =downloadConfigPayload.replace("%id%", model.yangId) + } + else { + downloadConfigPayload = contentFromResolvedArtifactNB("download-ne-sw") + model.yangId=model.targetSwVersion + } + downloadConfigPayload = downloadConfigPayload.replace("%actionName%", Action.DOWNLOAD_NE_SW.name) + log.info("Config Payload to start download : $downloadConfigPayload") + + //Apply configlet + restconfApplyDeviceConfig(model.client, model.deviceId, CONFIGLET_RESOURCE_PATH, downloadConfigPayload, + mutableMapOf("Content-Type" to "application/yang.patch+json")) + + //Poll PNF for Download action's progress + checkExecution(model) + } + + private suspend fun processActivateNeSw(model: SoftwareUpgradeModel) { + log.debug("In PNF SW upgrade : processActivateNeSw") + //Check if the software is downloaded and ready to be activated + if (checkIfSwReadyToPerformAction(Action.DOWNLOAD_NE_SW, model)) { + var activateConfigPayload: String = contentFromResolvedArtifactNB("configure") + activateConfigPayload = activateConfigPayload.replace("%actionName%", Action.ACTIVATE_NE_SW.name) + log.info("Config Payload to start activate : $activateConfigPayload") + //Apply configlet + restconfApplyDeviceConfig(model.client, model.deviceId, CONFIGLET_RESOURCE_PATH, activateConfigPayload, + mutableMapOf("Content-Type" to "application/yang.patch+json")) + + //Poll PNF for Activate action's progress + checkExecution(model) + } else { + throw BluePrintRetryException("Software Download not completed for device(${model.deviceId}) to activate sw version: ${model.targetSwVersion}") + } + } + + private suspend fun processPostcheck(model: SoftwareUpgradeModel) { + log.info("In PNF SW upgrade : processPostcheck") + //Log the current configuration for the subtree + if (checkIfSwReadyToPerformAction(Action.POST_CHECK, model)) { + log.info("PNF is healthy post activation!") + } + } + + private fun processCancel(model :SoftwareUpgradeModel) { + //This is for future implementation of cancel step during software upgrade + log.info("In PNF SW upgrade : processCancel") + } + + private suspend fun getCurrentConfig(model: SoftwareUpgradeModel) : ObjectNode{ + val currentConfig: BlueprintWebClientService.WebClientResponse = restconfDeviceConfig(model.client, model.deviceId, CONFIGLET_RESOURCE_PATH) + return JacksonUtils.jsonNode(currentConfig.body) as ObjectNode + } + private suspend fun checkExecution(model: SoftwareUpgradeModel) { + val checkExecutionBlock: suspend (Int) -> String = { + val result = restconfDeviceConfig(model.client, model.deviceId, TARGET_SOFTWARE_PATH.plus(model.yangId)) + if (result.body.contains(model.action.completionStatus)) { + log.info("${model.action.name} is complete") + result.body + } else { + throw BluePrintRetryException("Waiting for device(${model.deviceId}) to activate sw version ${model.targetSwVersion}") + } + } + model.client.retry(10, 0, 1000, checkExecutionBlock) + + } + + private suspend fun checkIfSwReadyToPerformAction(action : Action, model: SoftwareUpgradeModel): Boolean { + val configBody = getCurrentConfig(model) + configBody.get("software-upgrade")?.get("upgrade-package")?.iterator()?.forEach { item -> + if (model.targetSwVersion == item.get("software-version")?.asText() && + action.completionStatus == item?.get("current-status")?.asText()) { + model.yangId= item.get("id").textValue() + return true + } + } + return false + } + + override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) { + log.info("Recover function called!") + log.info("Execution request : $executionRequest") + log.error("Exception", runtimeException) + } +} + +enum class Action(val actionName: String, val completionStatus: String) { + PRE_CHECK("precheck", "INITIALIZED"), + DOWNLOAD_NE_SW("downloadNeSw", "DOWNLOAD_COMPLETED"), + ACTIVATE_NE_SW("activateNeSw", "ACTIVATION_COMPLETED"), + POST_CHECK("postcheck", "ACTIVATION_COMPLETED"), + CANCEL("cancel", "CANCELLED") + ; + companion object{ + fun getEnumFromActionName(name: String): Action { + for(value in values()){ + if (value.actionName==name) return value + } + throw BluePrintException("Invalid Action sent to CDS") + } + } +} + +data class SoftwareUpgradeModel(val resolutionKey: String, val client: BlueprintWebClientService, val deviceId: String, + val targetSwVersion: String, val action: Action, var yangId: String = "") \ No newline at end of file diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/TOSCA-Metadata/TOSCA.meta b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/TOSCA-Metadata/TOSCA.meta new file mode 100644 index 000000000..467964604 --- /dev/null +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/TOSCA-Metadata/TOSCA.meta @@ -0,0 +1,7 @@ +TOSCA-Meta-File-Version: 1.0.0 +CSAR-Version: 1.0 +Created-By: Raj Gumma +Entry-Definitions: Definitions/PNF_CDS_RESTCONF.json +Template-Name: PNF_CDS_RESTCONF +Template-Version: 1.0.0 +Template-Tags: PNF_CDS_RESTCONF diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Templates/config-assign-restconf-configlet-template.vtl b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/config-assign-restconf-configlet-template.vtl similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Templates/config-assign-restconf-configlet-template.vtl rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/config-assign-restconf-configlet-template.vtl diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/pnf-swug-config-template.vtl b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/pnf-swug-config-template.vtl new file mode 100644 index 000000000..5e52f6779 --- /dev/null +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/pnf-swug-config-template.vtl @@ -0,0 +1,22 @@ +{ + "ietf-restconf:yang-patch": { + "patch-id": "patch-1", + "edit": [ + { + "edit-id": "edit1", + "operation": "merge", + "target": "/", + "value": { + "software-upgrade": { + "upgrade-package": [ + { + "id": "%id%", + "action": "%actionName%" + } + ] + } + } + } + ] + } +} \ No newline at end of file diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/pnf-swug-download-ne-sw-template.vtl b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/pnf-swug-download-ne-sw-template.vtl new file mode 100644 index 000000000..695b66866 --- /dev/null +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/pnf-swug-download-ne-sw-template.vtl @@ -0,0 +1,28 @@ +{ + "ietf-restconf:yang-patch": { + "patch-id": "patch-1", + "edit": [ + { + "edit-id": "edit1", + "operation": "merge", + "target": "/", + "value": { + "software-upgrade": { + "upgrade-package": [ + { + "id": "${target-software-version}", + "current-status": "INITIALIZED", + "action": "%actionName%", + "user-label": "trial software update", + "uri": "sftp://127.0.0.1/test_software_2.img", + "software-version": "${target-software-version}", + "user": "test_user", + "password": "test_password" + } + ] + } + } + } + ] + } +} \ No newline at end of file diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Templates/config-deploy-restconf-mount-template.vtl b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/restconf-mount-template.vtl similarity index 100% rename from components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Templates/config-deploy-restconf-mount-template.vtl rename to components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Templates/restconf-mount-template.vtl diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Tests/uat.yaml b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Tests/uat.yaml new file mode 100644 index 000000000..79328e697 --- /dev/null +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Tests/uat.yaml @@ -0,0 +1,374 @@ +%YAML 1.1 +--- +processes: + - name: config-assign + request: + commonHeader: &commonHeader + originatorId: sdnc + requestId: "123456-1000" + subRequestId: sub-123456-1000 + actionIdentifiers: &assign-ai + blueprintName: PNF_CDS_RESTCONF + blueprintVersion: "1.0.0" + actionName: config-assign + mode: sync + payload: + config-assign-request: + resolution-key: &resKey "RES-KEY 61" + config-assign-properties: + service-instance-id: siid_1234 + pnf-id: &pnfId pnf-id-2019-07-12 + service-model-uuid: service-model-uuid + pnf-customization-uuid: pnf-customization-uuid + expectedResponse: + commonHeader: *commonHeader + actionIdentifiers: *assign-ai + status: + code: 200 + eventType: EVENT_COMPONENT_EXECUTED + errorMessage: null + message: success + payload: + config-assign-response: {} + - name: config-deploy + request: + commonHeader: *commonHeader + actionIdentifiers: &deploy-ai + actionName: config-deploy + blueprintName: PNF_CDS_RESTCONF + blueprintVersion: "1.0.0" + mode: sync + payload: + config-deploy-request: + resolution-key: *resKey + config-deploy-properties: + service-instance-id: siid_1234 + pnf-id: *pnfId + service-model-uuid: service-model-uuid + pnf-customization-uuid: pnf-customization-uuid + expectedResponse: + commonHeader: *commonHeader + actionIdentifiers: *deploy-ai + payload: + config-deploy-response: {} + status: + code: 200 + errorMessage: null + eventType: EVENT_COMPONENT_EXECUTED + message: success + - name: precheck + request: + commonHeader: &swugCommonHeader + originatorId: sdnc + requestId: "123456-1000" + subRequestId: sub-123456-1000 + actionIdentifiers: &precheck-ai + blueprintName: PNF_CDS_RESTCONF + blueprintVersion: "1.0.0" + actionName: precheck + mode: sync + payload: + precheck-request: + resolution-key: &resKey "RES-KEY 61" + precheck-properties: &actionProps + service-instance-id: siid_1234 + pnf-id: &pnfId pnf-id-2019-07-12 + target-software-version: "2.0.2" + service-model-uuid: service-model-uuid + pnf-customization-uuid: pnf-customization-uuid + expectedResponse: + commonHeader: *swugCommonHeader + actionIdentifiers: *precheck-ai + status: + code: 200 + eventType: EVENT_COMPONENT_EXECUTED + errorMessage: null + message: success + payload: + precheck-response: {} + - name: downloadNeSw + request: + commonHeader: *swugCommonHeader + actionIdentifiers: &download-ai + blueprintName: PNF_CDS_RESTCONF + blueprintVersion: "1.0.0" + actionName: downloadNeSw + mode: sync + payload: + downloadNeSw-request: + resolution-key: *resKey + downloadNeSw-properties: *actionProps + expectedResponse: + commonHeader: *swugCommonHeader + actionIdentifiers: *download-ai + status: + code: 200 + eventType: EVENT_COMPONENT_EXECUTED + errorMessage: null + message: success + payload: + downloadNeSw-response: {} + - name: activateNeSw + request: + commonHeader: *swugCommonHeader + actionIdentifiers: &activate-ai + blueprintName: PNF_CDS_RESTCONF + blueprintVersion: "1.0.0" + actionName: activateNeSw + mode: sync + payload: + activateNeSw-request: + resolution-key: *resKey + activateNeSw-properties: *actionProps + expectedResponse: + commonHeader: *swugCommonHeader + actionIdentifiers: *activate-ai + status: + code: 200 + eventType: EVENT_COMPONENT_EXECUTED + errorMessage: null + message: success + payload: + activateNeSw-response: {} + - name: postcheck + request: + commonHeader: *swugCommonHeader + actionIdentifiers: &postcheck-ai + blueprintName: PNF_CDS_RESTCONF + blueprintVersion: "1.0.0" + actionName: postcheck + mode: sync + payload: + postcheck-request: + resolution-key: *resKey + postcheck-properties: *actionProps + expectedResponse: + commonHeader: *swugCommonHeader + actionIdentifiers: *postcheck-ai + status: + code: 200 + eventType: EVENT_COMPONENT_EXECUTED + errorMessage: null + message: success + payload: + postcheck-response: {} +external-services: + - selector: aai-data + expectations: + - request: + method: GET + path: [ /aai/v14/network/pnfs/pnf, *pnfId] + headers: + Accept: application/json + response: + headers: + Content-Type: application/json + body: + ipaddress-v4-oam: &pnfAddress 13.13.13.13 + ipaddress-v6-oam: 1::13 + - selector: sdnc + expectations: + - request: + method: PUT + path: &configUri [ /restconf/config, &nodeIdentifier [network-topology:network-topology/topology/topology-netconf/node, *pnfId]] + headers: + Content-Type: application/json + body: + node: + - node-id: *pnfId + netconf-node-topology:protocol: { name: TLS } + netconf-node-topology:host: *pnfAddress + netconf-node-topology:key-based: + username: netconf + key-id: ODL_private_key_0 + netconf-node-topology:port: 6513 + netconf-node-topology:tcp-only: false + netconf-node-topology:max-connection-attempts: 5 + response: + status: 201 + - request: + method: GET + path: [ /restconf/operational, *nodeIdentifier] + response: + body: + node: [ { netconf-node-topology:connection-status: connected }] + - request: + method: GET + path: [*configUri, &configletResourcePath yang-ext:mount/mynetconf:netconflist] + response: + body: {} + - request: + method: PATCH + path: [*configUri, *configletResourcePath] + headers: + Content-Type: application/yang.patch+json + body: + ietf-restconf:yang-patch: + patch-id: patch-1 + edit: + - edit-id: edit1 + operation: merge + target: / + value: { netconflist: { netconf: [ { netconf-id: "10", netconf-param: "1000" }]}} + - edit-id: edit2 + operation: merge + target: / + value: { netconflist: { netconf: [ { netconf-id: "20", netconf-param: "2000" }]}} + - edit-id: edit3 + operation: merge + target: / + value: { netconflist: { netconf: [ { netconf-id: "30", netconf-param: "3000" }]}} + response: + body: + ietf-yang-patch:yang-patch-status: + patch-id: patch-1 + ok: [ + null + ] + ### External expectations for Software Upgrade + - request: + method: GET + path: &ConfigSwUgUri [*configUri, &configletResourcePath yang-ext:mount/pnf-sw-upgrade:software-upgrade] + headers: + Accept: application/json + responses: + - headers: + Content-Type: application/json + body: + software-upgrade: + upgrade-package: + - id: 2.0.1 + current-status: INITIALIZED + user-label: trial software update + uri: sftp:127.0.0.1/test_software_1.img + software-version: 2.0.1 + user: test_user + password: test_password + - headers: + Content-Type: application/json + body: + software-upgrade: + upgrade-package: + - id: 2.0.1 + current-status: INITIALIZED + user-label: trial software update + uri: sftp:127.0.0.1/test_software_1.img + software-version: 2.0.1 + user: test_user + password: test_password + - headers: + Content-Type: application/json + body: + software-upgrade: + upgrade-package: + - id: 2.0.1 + current-status: INITIALIZED + user-label: trial software update + uri: sftp:127.0.0.1/test_software_1.img + software-version: 2.0.1 + user: test_user + password: test_password + - id: 2.0.2 + current-status: DOWNLOAD_COMPLETED + state-change-time: '2020-02-20T13:03:21Z' + software-version: 2.0.2 + user-label: trial software update + uri: sftp:127.0.0.1/test_software_1.img + user: test_user + password: test_password + - headers: + Content-Type: application/json + body: + software-upgrade: + upgrade-package: + - id: 2.0.1 + current-status: INITIALIZED + user-label: trial software update + uri: sftp:127.0.0.1/test_software_1.img + software-version: 2.0.1 + user: test_user + password: test_password + - id: 2.0.2 + current-status: ACTIVATION_COMPLETED + state-change-time: '2020-02-20T13:03:21Z' + software-version: 2.0.2 + user-label: trial software update + uri: sftp:127.0.0.1/test_software_1.img + user: test_user + password: test_password + - request: + method: PATCH + path: *ConfigSwUgUri + headers: + Content-Type: application/yang.patch+json + body: + ietf-restconf:yang-patch: + patch-id: patch-1 + edit: + - edit-id: edit1 + operation: merge + target: "/" + response: + headers: + Content-Type: application/yang.patch-status+json + body: + { ietf-yang-patch:yang-patch-status: {patch-id: patch-1, ok: [ ] } } + times: 2 + - request: + method: GET + path: [*ConfigSwUgUri, upgrade-package/2.0.2] + headers: + Accept: application/json + responses: + - headers: + Content-Type: application/json + body: + upgrade-package: + - id: 2.0.2 + current-status: DOWNLOAD_IN_PROGRESS + state-change-time: '2020-02-20T12:17:34.984Z' + software-version: 2.0.2 + - headers: + Content-Type: application/json + body: + upgrade-package: + - id: 2.0.2 + current-status: DOWNLOAD_IN_PROGRESS + state-change-time: '2020-02-20T12:52:30Z' + software-version: 2.0.2 + - headers: + Content-Type: application/json + body: + upgrade-package: + - id: 2.0.2 + current-status: DOWNLOAD_COMPLETED + state-change-time: '2020-02-20T13:03:21Z' + software-version: 2.0.2 + - headers: + Content-Type: application/json + body: + upgrade-package: + - id: 2.0.2 + current-status: ACTIVATION_IN_PROGRESS + state-change-time: '2020-02-20T13:05:08Z' + software-version: 2.0.2 + - headers: + Content-Type: application/json + body: + upgrade-package: + - id: 2.0.2 + current-status: ACTIVATION_IN_PROGRESS + state-change-time: '2020-02-20T12:52:30Z' + software-version: 2.0.2 + - headers: + Content-Type: application/json + body: + upgrade-package: + - id: 2.0.2 + current-status: ACTIVATION_COMPLETED + state-change-time: '2020-02-20T13:07:12Z' + software-version: 2.0.2 + - request: + method: DELETE + path: *configUri + times: 5 \ No newline at end of file diff --git a/components/model-catalog/blueprint-model/uat-blueprints/README.md b/components/model-catalog/blueprint-model/uat-blueprints/README.md index 56cb32989..ffbc15aec 100644 --- a/components/model-catalog/blueprint-model/uat-blueprints/README.md +++ b/components/model-catalog/blueprint-model/uat-blueprints/README.md @@ -61,7 +61,8 @@ message Uat { message Expectation { required Request request = 1; - required Response response = 2; + optional Response response = 2; + repeated Response responses = 3; } message ExternalService { diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Tests/uat.yaml b/components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Tests/uat.yaml index 85b10c611..a58d089ad 100644 --- a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Tests/uat.yaml +++ b/components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Tests/uat.yaml @@ -64,7 +64,7 @@ external-services: expectations: - request: method: PUT - path: &configUri [ restconf/config, &nodeIdentifier [network-topology:network-topology/topology/topology-netconf/node, *pnfId]] + path: &configUri [ /restconf/config, &nodeIdentifier [network-topology:network-topology/topology/topology-netconf/node, *pnfId]] headers: Content-Type: application/json body: @@ -82,7 +82,7 @@ external-services: status: 201 - request: method: GET - path: [ restconf/operational, *nodeIdentifier] + path: [ /restconf/operational, *nodeIdentifier] response: body: node: [ { netconf-node-topology:connection-status: connected }] diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/data_types.json b/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/data_types.json deleted file mode 100644 index a0804bb3f..000000000 --- a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/data_types.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "data_types" : { - "dt-config-assign-properties" : { - "description" : "Dynamic DataType definition for workflow(config-assign).", - "version" : "1.0.0", - "properties" : { }, - "derived_from" : "tosca.datatypes.Dynamic" - }, - "dt-config-deploy-properties" : { - "description" : "Dynamic DataType definition for workflow(config-deploy).", - "version" : "1.0.0", - "properties" : { - "pnf-ipv4-address" : { - "type" : "string" - }, - "pnf-id" : { - "type" : "string" - } - }, - "derived_from" : "tosca.datatypes.Dynamic" - } - } -} \ No newline at end of file diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/pnf_config_aai.json b/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/pnf_config_aai.json deleted file mode 100644 index 3ef585cb4..000000000 --- a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Definitions/pnf_config_aai.json +++ /dev/null @@ -1,152 +0,0 @@ -{ - "tosca_definitions_version" : "controller_blueprint_1_0_0", - "metadata" : { - "template_author" : "Rahul Tyagi", - "author-email" : "rahul.tyagi@est.tech", - "user-groups" : "ADMIN, OPERATION", - "template_name" : "pnf_config_aai", - "template_version" : "1.0.0", - "template_tags" : "pnf, restconf, config, configuration" - }, - "imports" : [ { - "file" : "Definitions/data_types.json" - }, { - "file" : "Definitions/relationship_types.json" - }, { - "file" : "Definitions/artifact_types.json" - }, { - "file" : "Definitions/node_types.json" - }, { - "file" : "Definitions/policy_types.json" - } ], - "dsl_definitions" : { - "config-deploy-properties" : { - "resolution-key" : { - "get_input" : "resolution-key" - } - } - }, - "topology_template" : { - "workflows" : { - "config-assign" : { - "steps" : { - "activate-process" : { - "description" : "Create a configlet", - "target" : "config-assign", - "activities" : [ { - "call_operation" : "" - } ] - } - }, - "inputs" : { - "resolution-key" : { - "required" : true, - "type" : "string" - }, - "store-result" : { - "required" : true, - "type" : "boolean" - }, - "config-assign-properties" : { - "description" : "Dynamic PropertyDefinition for workflow(config-assign).", - "required" : true, - "type" : "dt-config-assign-properties" - } - } - }, - "config-deploy" : { - "steps" : { - "activate-process" : { - "description" : "Send a configlet to the pnf", - "target" : "config-deploy", - "activities" : [ { - "call_operation" : "" - } ] - } - }, - "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" : { - "config-assign" : { - "type" : "component-resource-resolution", - "interfaces" : { - "ResourceResolutionComponent" : { - "operations" : { - "process" : { - "inputs" : { - "resolution-key" : { - "get_input" : "resolution-key" - }, - "store-result" : true, - "artifact-prefix-names" : [ "config-assign" ] - }, - "outputs" : { - "resource-assignment-params" : { - "get_attribute" : [ "SELF", "assignment-params" ] - }, - "status" : "success" - } - } - } - } - }, - "artifacts" : { - "config-assign-template" : { - "type" : "artifact-template-velocity", - "file" : "Templates/config-assign-restconf-configlet-template.vtl" - }, - "config-assign-mapping" : { - "type" : "artifact-mapping-resource", - "file" : "Definitions/config-assign-pnf-mapping.json" - } - } - }, - "config-deploy" : { - "type" : "component-script-executor", - "interfaces" : { - "ComponentScriptExecutor" : { - "operations" : { - "process" : { - "implementation" : { - "primary" : "component-script", - "timeout" : 180, - "operation_host" : "SELF" - }, - "inputs" : { - "script-type" : "kotlin", - "script-class-reference" : "cba.pnf.config.aai.RestconfConfigDeploy", - "dynamic-properties" : "*config-deploy-properties" - }, - "outputs" : { - "response-data" : "", - "status" : "success" - } - } - } - } - }, - "artifacts" : { - "config-deploy-template" : { - "type" : "artifact-template-velocity", - "file" : "Templates/config-deploy-restconf-mount-template.vtl" - }, - "config-deploy-mapping" : { - "type" : "artifact-mapping-resource", - "file" : "Definitions/config-deploy-pnf-mapping.json" - } - } - } - } - } -} \ No newline at end of file diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/TOSCA-Metadata/TOSCA.meta b/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/TOSCA-Metadata/TOSCA.meta deleted file mode 100644 index 903600836..000000000 --- a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/TOSCA-Metadata/TOSCA.meta +++ /dev/null @@ -1,7 +0,0 @@ -TOSCA-Meta-File-Version: 1.0.0 -CSAR-Version: 1.0 -Created-By: Rahul Tyagi -Entry-Definitions: Definitions/pnf_config_aai.json -Template-Name: pnf_config_aai -Template-Version: 1.0.0 -Template-Tags: pnf_config_aai diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Tests/uat.yaml b/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Tests/uat.yaml deleted file mode 100644 index 13e10f34b..000000000 --- a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config_aai/Tests/uat.yaml +++ /dev/null @@ -1,137 +0,0 @@ -%YAML 1.1 ---- -processes: - - name: config-assign - request: - commonHeader: &commonHeader - originatorId: sdnc - requestId: "123456-1000" - subRequestId: sub-123456-1000 - actionIdentifiers: &assign-ai - blueprintName: pnf_config_aai - blueprintVersion: "1.0.0" - actionName: config-assign - mode: sync - payload: - config-assign-request: - resolution-key: &resKey "RES-KEY 61" - config-assign-properties: - service-instance-id: siid_1234 - pnf-id: &pnfId pnf-id-2019-07-12 - service-model-uuid: service-model-uuid - pnf-customization-uuid: pnf-customization-uuid - expectedResponse: - commonHeader: *commonHeader - actionIdentifiers: *assign-ai - status: - code: 200 - eventType: EVENT_COMPONENT_EXECUTED - errorMessage: null - message: success - payload: - config-assign-response: {} - - name: config-deploy - request: - commonHeader: *commonHeader - actionIdentifiers: &deploy-ai - actionName: config-deploy - blueprintName: pnf_config_aai - blueprintVersion: "1.0.0" - mode: sync - payload: - config-deploy-request: - resolution-key: *resKey - config-deploy-properties: - service-instance-id: siid_1234 - pnf-id: *pnfId - service-model-uuid: service-model-uuid - pnf-customization-uuid: pnf-customization-uuid - expectedResponse: - commonHeader: *commonHeader - actionIdentifiers: *deploy-ai - payload: - config-deploy-response: {} - status: - code: 200 - errorMessage: null - eventType: EVENT_COMPONENT_EXECUTED - message: success - -external-services: - - selector: aai-data - expectations: - - request: - method: GET - path: [ /aai/v14/network/pnfs/pnf, *pnfId] - headers: - Accept: application/json - response: - headers: - Content-Type: application/json - body: - ipaddress-v4-oam: &pnfAddress 13.13.13.13 - ipaddress-v6-oam: 1::13 - - selector: sdnc - expectations: - - request: - method: PUT - path: &configUri [ restconf/config, &nodeIdentifier [network-topology:network-topology/topology/topology-netconf/node, *pnfId]] - headers: - Content-Type: application/json - body: - node: - - node-id: *pnfId - netconf-node-topology:protocol: { name: TLS } - netconf-node-topology:host: *pnfAddress - netconf-node-topology:key-based: - username: netconf - key-id: ODL_private_key_0 - netconf-node-topology:port: 6513 - netconf-node-topology:tcp-only: false - netconf-node-topology:max-connection-attempts: 5 - response: - status: 201 - - request: - method: GET - path: [ restconf/operational, *nodeIdentifier] - response: - body: - node: [ { netconf-node-topology:connection-status: connected }] - - request: - method: GET - path: [*configUri, &configletResourcePath yang-ext:mount/mynetconf:netconflist] - response: - body: {} - - request: - method: PATCH - path: [*configUri, *configletResourcePath] - headers: - Content-Type: application/yang.patch+json - body: - ietf-restconf:yang-patch: - patch-id: patch-1 - edit: - - edit-id: edit1 - operation: merge - target: / - value: { netconflist: { netconf: [ { netconf-id: "10", netconf-param: "1000" }]}} - - edit-id: edit2 - operation: merge - target: / - value: { netconflist: { netconf: [ { netconf-id: "20", netconf-param: "2000" }]}} - - edit-id: edit3 - operation: merge - target: / - value: { netconflist: { netconf: [ { netconf-id: "30", netconf-param: "3000" }]}} - response: - body: - ietf-yang-patch:yang-patch-status: - patch-id: patch-1 - ok: [ - null - ] - - request: - method: DELETE - path: *configUri - - diff --git a/ms/blueprintsprocessor/application/src/main/resources/application-local.yml b/ms/blueprintsprocessor/application/src/main/resources/application-local.yml index de2cf4e52..f2843322c 100644 --- a/ms/blueprintsprocessor/application/src/main/resources/application-local.yml +++ b/ms/blueprintsprocessor/application/src/main/resources/application-local.yml @@ -38,10 +38,10 @@ blueprintsprocessor: remoteScriptCommand: enabled: true restclient: - sdncodl: + sdnc: password: Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U type: basic-auth - url: http://localhost:8282/ + url: http://localhost:8282 username: admin restconfEnabled: true controllerblueprints: diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/UatServicesTest.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/UatServicesTest.kt index aebda8c07..4e7d4ce40 100644 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/UatServicesTest.kt +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/UatServicesTest.kt @@ -30,6 +30,8 @@ import com.github.tomakehurst.wiremock.client.WireMock.equalToJson import com.github.tomakehurst.wiremock.client.WireMock.request import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo import com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig +import com.github.tomakehurst.wiremock.http.HttpHeader +import com.github.tomakehurst.wiremock.http.HttpHeaders import org.apache.http.HttpStatus import org.apache.http.client.methods.HttpPost import org.apache.http.entity.ContentType @@ -55,8 +57,6 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.web.server.LocalServerPort import org.springframework.core.env.ConfigurableEnvironment import org.springframework.core.env.MapPropertySource -import org.springframework.http.HttpHeaders -import org.springframework.http.MediaType import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.support.TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME import org.yaml.snakeyaml.Yaml @@ -211,7 +211,6 @@ class UatServicesTest : BaseUatTest() { service.expectations.forEach { expectation -> val request = expectation.request - val response = expectation.response // WebTestClient always use absolute path, prefixing with "/" if necessary val urlPattern = urlEqualTo(request.path.prefixIfNot("/")) val mappingBuilder: MappingBuilder = request(request.method, urlPattern) @@ -222,15 +221,19 @@ class UatServicesTest : BaseUatTest() { mappingBuilder.withRequestBody(equalToJson(mapper.writeValueAsString(request.body), true, true)) } - val responseDefinitionBuilder: ResponseDefinitionBuilder = aResponse() - .withStatus(response.status) - if (response.body != null) { - responseDefinitionBuilder.withBody(mapper.writeValueAsBytes(response.body)) - .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + for (response in expectation.responses) { + val responseDefinitionBuilder: ResponseDefinitionBuilder = aResponse() + .withStatus(response.status) + if (response.body != null) { + responseDefinitionBuilder.withBody(mapper.writeValueAsBytes(response.body)) + .withHeaders(HttpHeaders( + response.headers.entries.map { e -> HttpHeader(e.key, e.value) })) + } + + // TODO: MockServer verification for multiple responses should be done using Wiremock scenarios + mappingBuilder.willReturn(responseDefinitionBuilder) } - mappingBuilder.willReturn(responseDefinitionBuilder) - mockServer.stubFor(mappingBuilder) } return mockServer diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/InvalidUatDefinition.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/InvalidUatDefinition.kt new file mode 100644 index 000000000..4a756411f --- /dev/null +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/InvalidUatDefinition.kt @@ -0,0 +1,22 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.ccsdk.cds.blueprintsprocessor.uat.utils + +class InvalidUatDefinition(message: String) : RuntimeException(message) diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatDefinition.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatDefinition.kt index c45ac45c6..17b79f588 100644 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatDefinition.kt +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatDefinition.kt @@ -47,18 +47,30 @@ data class RequestDefinition( ) @JsonInclude(JsonInclude.Include.NON_EMPTY) -data class ResponseDefinition(val status: Int = 200, val body: JsonNode? = null) { +data class ResponseDefinition(val status: Int = 200, val body: JsonNode? = null, val headers: Map = mapOf("Content-Type" to "application/json")) { companion object { - val DEFAULT_RESPONSE = ResponseDefinition() + val DEFAULT_RESPONSES = listOf(ResponseDefinition()) } } @JsonInclude(JsonInclude.Include.NON_EMPTY) -data class ExpectationDefinition( +class ExpectationDefinition( val request: RequestDefinition, - val response: ResponseDefinition = ResponseDefinition.DEFAULT_RESPONSE -) + response: ResponseDefinition?, + responses: List? = null, + val times: String = ">= 1" +) { + val responses: List = resolveOneOrMany(response, responses, ResponseDefinition.DEFAULT_RESPONSES) + + companion object { + fun resolveOneOrMany(one: T?, many: List?, defaultMany: List): List = when { + many != null -> many + one != null -> listOf(one) + else -> defaultMany + } + } +} @JsonInclude(JsonInclude.Include.NON_EMPTY) data class ServiceDefinition(val selector: String, val expectations: List) @@ -97,6 +109,6 @@ data class UatDefinition( companion object { fun load(mapper: ObjectMapper, spec: String): UatDefinition = - mapper.convertValue(Yaml().load(spec), UatDefinition::class.java) + mapper.convertValue(Yaml().load(spec), UatDefinition::class.java) } } diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatExecutor.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatExecutor.kt index a904fa9b6..d120e71d6 100644 --- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatExecutor.kt +++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/uat/utils/UatExecutor.kt @@ -24,9 +24,10 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.argThat import com.nhaarman.mockitokotlin2.atLeast -import com.nhaarman.mockitokotlin2.atLeastOnce +import com.nhaarman.mockitokotlin2.atMost import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions import com.nhaarman.mockitokotlin2.whenever @@ -44,6 +45,7 @@ import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.notNullValue import org.hamcrest.MatcherAssert.assertThat import org.mockito.Answers +import org.mockito.verification.VerificationMode import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BluePrintRestLibPropertyService import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService.WebClientResponse @@ -77,7 +79,8 @@ class UatExecutor( companion object { private const val NOOP_PASSWORD_PREFIX = "{noop}" - + private const val PROPERTY_IN_UAT = "IN_UAT" + private val TIMES_SPEC_REGEX = "([<>]=?)?\\s*(\\d+)".toRegex() private val log: Logger = LoggerFactory.getLogger(UatExecutor::class.java) private val mockLoggingListener = MockInvocationLogger(markerOf(COLOR_MOCKITO)) } @@ -103,23 +106,24 @@ class UatExecutor( fun execute(uat: UatDefinition, cbaBytes: ByteArray): UatDefinition { val defaultHeaders = listOf(BasicHeader(HttpHeaders.AUTHORIZATION, clientAuthToken())) val httpClient = HttpClientBuilder.create() - .setDefaultHeaders(defaultHeaders) - .build() + .setDefaultHeaders(defaultHeaders) + .build() // Only if externalServices are defined val mockInterceptor = MockPreInterceptor() // Always defined and used, whatever the case val spyInterceptor = SpyPostInterceptor(mapper) restClientFactory.setInterceptors(mockInterceptor, spyInterceptor) try { - // Configure mocked external services and save their expected requests for further validation - val requestsPerClient = uat.externalServices.associateBy( - { service -> - createRestClientMock(service.expectations).also { restClient -> - // side-effect: register restClient to override real instance - mockInterceptor.registerMock(service.selector, restClient) - } - }, - { service -> service.expectations.map { it.request } } + markUatBegin() + // Configure mocked external services and save their expectations for further validation + val expectationsPerClient = uat.externalServices.associateBy( + { service -> + createRestClientMock(service.expectations).also { restClient -> + // side-effect: register restClient to override real instance + mockInterceptor.registerMock(service.selector, restClient) + } + }, + { service -> service.expectations } ) val newProcesses = httpClient.use { client -> @@ -130,26 +134,27 @@ class UatExecutor( log.info("Executing process '${process.name}'") val responseNormalizer = JsonNormalizer.getNormalizer(mapper, process.responseNormalizerSpec) val actualResponse = processBlueprint( - client, process.request, - process.expectedResponse, responseNormalizer + client, process.request, + process.expectedResponse, responseNormalizer ) ProcessDefinition( - process.name, - process.request, - actualResponse, - process.responseNormalizerSpec + process.name, + process.request, + actualResponse, + process.responseNormalizerSpec ) } } // Validate requests to external services - for ((mockClient, requests) in requestsPerClient) { - requests.forEach { request -> - verify(mockClient, atLeastOnce()).exchangeResource( - eq(request.method), - eq(request.path), - argThat { assertJsonEquals(request.body, this) }, - argThat(RequiredMapEntriesMatcher(request.headers)) + for ((mockClient, expectations) in expectationsPerClient) { + expectations.forEach { expectation -> + val request = expectation.request + verify(mockClient, evalVerificationMode(expectation.times)).exchangeResource( + eq(request.method), + eq(request.path), + argThat { assertJsonEquals(request.body, this) }, + argThat(RequiredMapEntriesMatcher(request.headers)) ) } // Don't mind the invocations to the overloaded exchangeResource(String, String, String) @@ -158,40 +163,51 @@ class UatExecutor( } val newExternalServices = spyInterceptor.getSpies() - .map(SpyService::asServiceDefinition) + .map(SpyService::asServiceDefinition) return UatDefinition(newProcesses, newExternalServices) } finally { restClientFactory.clearInterceptors() + markUatEnd() } } + private fun markUatBegin() { + System.setProperty(PROPERTY_IN_UAT, "1") + } + + private fun markUatEnd() { + System.clearProperty(PROPERTY_IN_UAT) + } + private fun createRestClientMock(restExpectations: List): BlueprintWebClientService { val restClient = mock( - defaultAnswer = Answers.RETURNS_SMART_NULLS, - // our custom verboseLogging handler - invocationListeners = arrayOf(mockLoggingListener) + defaultAnswer = Answers.RETURNS_SMART_NULLS, + // our custom verboseLogging handler + invocationListeners = arrayOf(mockLoggingListener) ) // Delegates to overloaded exchangeResource(String, String, String, Map) whenever(restClient.exchangeResource(any(), any(), any())) - .thenAnswer { invocation -> - val method = invocation.arguments[0] as String - val path = invocation.arguments[1] as String - val request = invocation.arguments[2] as String - restClient.exchangeResource(method, path, request, emptyMap()) - } + .thenAnswer { invocation -> + val method = invocation.arguments[0] as String + val path = invocation.arguments[1] as String + val request = invocation.arguments[2] as String + restClient.exchangeResource(method, path, request, emptyMap()) + } for (expectation in restExpectations) { - whenever( - restClient.exchangeResource( - eq(expectation.request.method), - eq(expectation.request.path), - any(), - any() - ) + var stubbing = whenever( + restClient.exchangeResource( + eq(expectation.request.method), + eq(expectation.request.path), + any(), + any() + ) ) - .thenReturn(WebClientResponse(expectation.response.status, expectation.response.body.toString())) + for (response in expectation.responses) { + stubbing = stubbing.thenReturn(WebClientResponse(response.status, response.body.toString())) + } } return restClient } @@ -199,9 +215,9 @@ class UatExecutor( @Throws(AssertionError::class) private fun uploadBlueprint(client: HttpClient, cbaBytes: ByteArray) { val multipartEntity = MultipartEntityBuilder.create() - .setMode(HttpMultipartMode.BROWSER_COMPATIBLE) - .addBinaryBody("file", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip") - .build() + .setMode(HttpMultipartMode.BROWSER_COMPATIBLE) + .addBinaryBody("file", cbaBytes, ContentType.DEFAULT_BINARY, "cba.zip") + .build() val request = HttpPost("$baseUrl/api/v1/blueprint-model/publish").apply { entity = multipartEntity } @@ -236,6 +252,19 @@ class UatExecutor( return mapper.readTree(actualResponse)!! } + private fun evalVerificationMode(times: String): VerificationMode { + val matchResult = TIMES_SPEC_REGEX.matchEntire(times) ?: throw InvalidUatDefinition( + "Time specification '$times' does not follow expected format $TIMES_SPEC_REGEX") + val counter = matchResult.groups[2]!!.value.toInt() + return when (matchResult.groups[1]?.value) { + ">=" -> atLeast(counter) + ">" -> atLeast(counter + 1) + "<=" -> atMost(counter) + "<" -> atMost(counter - 1) + else -> times(counter) + } + } + @Throws(AssertionError::class) private fun assertJsonEquals(expected: JsonNode?, actual: String): Boolean { // special case @@ -249,15 +278,15 @@ class UatExecutor( } private fun localServerPort(): Int = - (environment.getProperty("local.server.port") - ?: environment.getRequiredProperty("blueprint.httpPort")).toInt() + (environment.getProperty("local.server.port") + ?: environment.getRequiredProperty("blueprint.httpPort")).toInt() private fun clientAuthToken(): String { val username = environment.getRequiredProperty("security.user.name") val password = environment.getRequiredProperty("security.user.password") val plainPassword = when { password.startsWith(NOOP_PASSWORD_PREFIX) -> password.substring( - NOOP_PASSWORD_PREFIX.length) + NOOP_PASSWORD_PREFIX.length) else -> username } return "Basic " + Base64Utils.encodeToString("$username:$plainPassword".toByteArray()) @@ -271,7 +300,7 @@ class UatExecutor( } override fun getInstance(selector: String): BlueprintWebClientService? = - mocks[selector] + mocks[selector] fun registerMock(selector: String, client: BlueprintWebClientService) { mocks[selector] = client @@ -293,7 +322,7 @@ class UatExecutor( } fun getSpies(): List = - spies.values.toList() + spies.values.toList() } private class SpyService( @@ -301,14 +330,14 @@ class UatExecutor( val selector: String, private val realService: BlueprintWebClientService ) : - BlueprintWebClientService by realService { + BlueprintWebClientService by realService { private val expectations: MutableList = mutableListOf() override fun exchangeResource(methodType: String, path: String, request: String): WebClientResponse = - exchangeResource(methodType, path, request, - DEFAULT_HEADERS - ) + exchangeResource(methodType, path, request, + DEFAULT_HEADERS + ) override fun exchangeResource( methodType: String, @@ -317,7 +346,7 @@ class UatExecutor( headers: Map ): WebClientResponse { val requestDefinition = - RequestDefinition(methodType, path, headers, toJson(request)) + RequestDefinition(methodType, path, headers, toJson(request)) val realAnswer = realService.exchangeResource(methodType, path, request, headers) val responseBody = when { // TODO: confirm if we need to normalize the response here @@ -325,12 +354,12 @@ class UatExecutor( else -> null } val responseDefinition = - ResponseDefinition(realAnswer.status, responseBody) + ResponseDefinition(realAnswer.status, responseBody) expectations.add( - ExpectationDefinition( - requestDefinition, - responseDefinition - ) + ExpectationDefinition( + requestDefinition, + responseDefinition + ) ) return realAnswer } @@ -340,7 +369,7 @@ class UatExecutor( } fun asServiceDefinition() = - ServiceDefinition(selector, expectations) + ServiceDefinition(selector, expectations) private fun toJson(str: String): JsonNode? { return when { @@ -351,8 +380,8 @@ class UatExecutor( companion object { private val DEFAULT_HEADERS = mapOf( - HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE, - HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE + HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE, + HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE ) } } diff --git a/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutorExtensions.kt b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutorExtensions.kt index fc6d5a910..408eaf45b 100644 --- a/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutorExtensions.kt +++ b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutorExtensions.kt @@ -45,12 +45,12 @@ suspend fun AbstractScriptComponentFunction.restconfMountDevice( headers: Map = mutableMapOf("Content-Type" to "application/xml") ) { - val mountUrl = "restconf/config/network-topology:network-topology/topology/topology-netconf/node/$deviceId" + val mountUrl = "/restconf/config/network-topology:network-topology/topology/topology-netconf/node/$deviceId" log.info("sending mount request, url: $mountUrl") webClientService.exchangeResource("PUT", mountUrl, payload as String, headers) /** Check device has mounted */ - val mountCheckUrl = "restconf/operational/network-topology:network-topology/topology/topology-netconf/node/$deviceId" + val mountCheckUrl = "/restconf/operational/network-topology:network-topology/topology/topology-netconf/node/$deviceId" val expectedResult = """"netconf-node-topology:connection-status":"connected"""" val mountCheckExecutionBlock: suspend (Int) -> String = { tryCount: Int -> @@ -80,7 +80,7 @@ suspend fun AbstractScriptComponentFunction.restconfApplyDeviceConfig( ): BlueprintWebClientService.WebClientResponse { log.debug("headers: $additionalHeaders") log.info("configuring device: $deviceId, Configlet: $configletToApply") - val applyConfigUrl = "restconf/config/network-topology:network-topology/topology/topology-netconf/node/" + + val applyConfigUrl = "/restconf/config/network-topology:network-topology/topology/topology-netconf/node/" + "$deviceId/$configletResourcePath" return webClientService.exchangeResource("PATCH", applyConfigUrl, configletToApply as String, additionalHeaders) } @@ -92,7 +92,7 @@ suspend fun AbstractScriptComponentFunction.restconfDeviceConfig( ): BlueprintWebClientService.WebClientResponse { - val configPathUrl = "restconf/config/network-topology:network-topology/topology/topology-netconf/node/" + + val configPathUrl = "/restconf/config/network-topology:network-topology/topology/topology-netconf/node/" + "$deviceId/$configletResourcePath" log.debug("sending GET request, url: $configPathUrl") return webClientService.exchangeResource("GET", configPathUrl, "") @@ -106,7 +106,7 @@ suspend fun AbstractScriptComponentFunction.restconfUnMountDevice( deviceId: String, payload: String ) { - val unMountUrl = "restconf/config/network-topology:network-topology/topology/topology-netconf/node/$deviceId" + val unMountUrl = "/restconf/config/network-topology:network-topology/topology/topology-netconf/node/$deviceId" log.info("sending unMount request, url: $unMountUrl") webClientService.exchangeResource("DELETE", unMountUrl, "") } diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/cluster/HazlecastClusterService.kt b/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/cluster/HazlecastClusterService.kt index 6be3334bb..a58c077fa 100644 --- a/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/cluster/HazlecastClusterService.kt +++ b/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/cluster/HazlecastClusterService.kt @@ -243,8 +243,11 @@ open class ClusterLockImpl(private val hazelcast: HazelcastInstance, private val } override suspend fun unLock() { - distributedLock.unlock() - log.trace("Cluster unlock(${name()}) successfully..") + // Added condition to avoid failures like - "Current thread is not owner of the lock!" + if (distributedLock.isLockedByCurrentThread) { + distributedLock.unlock() + log.trace("Cluster unlock(${name()}) successfully..") + } } override fun isLocked(): Boolean { diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/BasicAuthRestClientService.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/BasicAuthRestClientService.kt index 540b3d9ad..bfc0a5cd4 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/BasicAuthRestClientService.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/rest/service/BasicAuthRestClientService.kt @@ -20,6 +20,7 @@ import org.apache.http.message.BasicHeader import org.onap.ccsdk.cds.blueprintsprocessor.rest.BasicAuthRestClientProperties import org.springframework.http.HttpHeaders import org.springframework.http.MediaType +import java.net.URI import java.nio.charset.Charset import java.util.Base64 @@ -43,7 +44,8 @@ class BasicAuthRestClientService( } override fun host(uri: String): String { - return restClientProperties.url + uri + val uri: URI = URI.create(restClientProperties.url + uri) + return uri.resolve(uri).toString() } override fun convertToBasicHeaders(headers: Map):