diff options
Diffstat (limited to 'test/mocks/emssimulator')
35 files changed, 1981 insertions, 664 deletions
diff --git a/test/mocks/emssimulator/install.sh b/test/mocks/emssimulator/install.sh index e3069aae2..64982d9a1 100755 --- a/test/mocks/emssimulator/install.sh +++ b/test/mocks/emssimulator/install.sh @@ -4,13 +4,13 @@ readonly old_pwd=$PWD cd swm/sw_server_simulator for i in `ls -1`; do - if [ -d $i ]; then - cd $i - echo $i - zip -r ${i}.zip * - mv ${i}.zip .. - cd $OLDPWD - fi + if [ -d $i ]; then + cd $i + echo $i + zip -r ${i}.zip * + mv ${i}.zip .. + cd $OLDPWD + fi done cd $old_pwd diff --git a/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/build-cba-for-pnf-sw-upgrade-with-em.sh b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/build-cba-for-pnf-sw-upgrade-with-em.sh new file mode 100755 index 000000000..76102c3b1 --- /dev/null +++ b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/build-cba-for-pnf-sw-upgrade-with-em.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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========================================================= + +set -euo pipefail + +readonly SOURCE_CBA="PNF_CDS_RESTCONF" +readonly TARGET_CBA="PNF_SW_UPGRADE_WITH_EM" +readonly CDS_CODE_DIR="cds-codes-for-pnf-sw-upgrade" + +function use_specific_commit() +{ + local commit_id="$1" + cd ${CDS_CODE_DIR} + local tmp_branch="get-cba-for-pnf-sw-upgrade" + if ! git branch | grep -q "${tmp_branch}"; then + git checkout -b ${tmp_branch} ${commit_id} + else + git checkout -q ${tmp_branch} + fi + cd ${OLDPWD} +} + +if [ ! -e ${CDS_CODE_DIR} ]; then + git clone https://gerrit.onap.org/r/ccsdk/cds ${CDS_CODE_DIR} +else + cd ${CDS_CODE_DIR} + code_status=`git status -s` + if [ -n "${code_status}" ]; then + echo "Please keep the CDS codes are not modified." + exit 1 + fi + cd ${OLDPWD} +fi + +# Lock the version of source CBA files +use_specific_commit f4ac359d80d043a2d0e6eaf1730813b81f2c837f + +if [ -e ${TARGET_CBA} -o -e ${TARGET_CBA}.zip ]; then + echo "${TARGET_CBA} or ${TARGET_CBA}.zip has existed, please rename or delete them." + exit 1 +fi + +cp -ir ${CDS_CODE_DIR}/components/model-catalog/blueprint-model/uat-blueprints/${SOURCE_CBA} ${TARGET_CBA} +cp -ir patches ${TARGET_CBA} + +cd ${TARGET_CBA} + +mv Definitions/PNF_CDS_RESTCONF.json Definitions/PNF_SW_UPGRADE_WITH_EM.json +mv Templates/pnf-swug-config-template.vtl Templates/pnf-swug-activate-ne-sw-template.vtl + +for p in patches/*.patch; do + patch -p1 -i $p +done + +rm -rf patches + +zip -r ${TARGET_CBA}.zip . + +cd ${OLDPWD} + +mv -i ${TARGET_CBA}/${TARGET_CBA}.zip . + diff --git a/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/PNF_SW_UPGRADE_WITH_EM.json.patch b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/PNF_SW_UPGRADE_WITH_EM.json.patch new file mode 100644 index 000000000..f4c80624e --- /dev/null +++ b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/PNF_SW_UPGRADE_WITH_EM.json.patch @@ -0,0 +1,148 @@ +--- PNF_CDS_RESTCONF/Definitions/PNF_CDS_RESTCONF.json 2020-03-14 20:54:46.677546900 +0800 ++++ PNF_SW_UPGRADE_WITH_EM/Definitions/PNF_SW_UPGRADE_WITH_EM.json 2020-03-14 22:08:47.225398400 +0800 +@@ -4,7 +4,7 @@ + "template_author" : "Raj Gumma", + "author-email" : "raj.gumma@est.tech", + "user-groups" : "ADMIN, OPERATION", +- "template_name" : "PNF_CDS_RESTCONF", ++ "template_name" : "PNF_SW_UPGRADE_WITH_EM", + "template_version" : "1.0.0", + "template_tags" : "PNF, Restconf, config, configuration, software upgrade" + }, +@@ -74,28 +74,6 @@ + } + } + }, +- "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" : { +@@ -220,41 +198,6 @@ + } + } + }, +- "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" : { +@@ -268,7 +211,7 @@ + }, + "inputs" : { + "script-type" : "kotlin", +- "script-class-reference" : "cba.pnf.swug.RestconfSoftwareUpgrade", ++ "script-class-reference" : "cba.pnf.swm.RestconfSoftwareUpgrade", + "dynamic-properties" : "*preCheck-properties" + }, + "outputs" : { +@@ -303,7 +246,7 @@ + }, + "inputs" : { + "script-type" : "kotlin", +- "script-class-reference" : "cba.pnf.swug.RestconfSoftwareUpgrade", ++ "script-class-reference" : "cba.pnf.swm.RestconfSoftwareUpgrade", + "dynamic-properties" : "*downloadNESw-properties" + }, + "outputs" : { +@@ -323,14 +266,6 @@ + "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" +@@ -354,7 +289,7 @@ + }, + "inputs" : { + "script-type" : "kotlin", +- "script-class-reference" : "cba.pnf.swug.RestconfSoftwareUpgrade", ++ "script-class-reference" : "cba.pnf.swm.RestconfSoftwareUpgrade", + "dynamic-properties" : "*activateNESw-properties" + }, + "outputs" : { +@@ -374,11 +309,11 @@ + "type" : "artifact-mapping-resource", + "file" : "Definitions/pnf-software-upgrade-mapping.json" + }, +- "configure-template" : { ++ "activate-ne-sw-template" : { + "type" : "artifact-template-velocity", +- "file" : "Templates/pnf-swug-config-template.vtl" ++ "file" : "Templates/pnf-swug-activate-ne-sw-template.vtl" + }, +- "configure-mapping" : { ++ "activate-ne-sw-mapping" : { + "type" : "artifact-mapping-resource", + "file" : "Definitions/pnf-software-upgrade-mapping.json" + } +@@ -397,7 +332,7 @@ + }, + "inputs" : { + "script-type" : "kotlin", +- "script-class-reference" : "cba.pnf.swug.RestconfSoftwareUpgrade", ++ "script-class-reference" : "cba.pnf.swm.RestconfSoftwareUpgrade", + "dynamic-properties" : "*postCheck-properties" + }, + "outputs" : { diff --git a/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/RestconfSoftwareUpgrade.kt.patch b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/RestconfSoftwareUpgrade.kt.patch new file mode 100644 index 000000000..01473f4ad --- /dev/null +++ b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/RestconfSoftwareUpgrade.kt.patch @@ -0,0 +1,192 @@ +--- PNF_CDS_RESTCONF/Scripts/kotlin/RestconfSoftwareUpgrade.kt 2020-03-12 15:16:34.617000000 +0800 ++++ PNF_SW_UPGRADE_WITH_EM/Scripts/kotlin/RestconfSoftwareUpgrade.kt 2020-03-12 23:12:50.012507800 +0800 +@@ -1,6 +1,7 @@ + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation. ++* Modifications Copyright (C) 2020 Huawei Technologies Co., Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. +@@ -17,7 +18,7 @@ + */ + + +-package cba.pnf.swug ++package cba.pnf.swm + + import com.fasterxml.jackson.databind.node.ObjectNode + import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput +@@ -38,20 +39,27 @@ + class RestconfSoftwareUpgrade : AbstractScriptComponentFunction() { + + private val RESTCONF_SERVER_IDENTIFIER = "sdnc" +- private val CONFIGLET_RESOURCE_PATH = "yang-ext:mount/pnf-sw-upgrade:software-upgrade" ++ private val CONFIGLET_RESOURCE_PATH = "yang-ext:mount/pnf-swm:software-management" + private val log = logger(AbstractScriptComponentFunction::class.java) +- private val TARGET_SOFTWARE_PATH = "$CONFIGLET_RESOURCE_PATH/upgrade-package/" ++ private val TARGET_SOFTWARE_PATH = "$CONFIGLET_RESOURCE_PATH/pnf-software-package/" + + override suspend fun processNB(executionRequest: ExecutionServiceInput) { + + // Extract request properties +- val model= validatedPayload(executionRequest) ++ var model: SoftwareManagementModel = validatedPayload(executionRequest) + + log.info("Blueprint invoked for ${model.resolutionKey} for SW Upgrade : " + +- "${model.action} for sw version ${model.targetSwVersion} on pnf: ${model.deviceId}") ++ "${model.action} for sw version ${model.targetSwVersion} on pnf: ${model.neIdentifier}") + + try { +- val mountPayload = contentFromResolvedArtifactNB("mount-node") ++ var mountPayload: String = contentFromResolvedArtifactNB("mount-node") ++ ++ val mountPayloadObject = JacksonUtils.jsonNode(mountPayload) as ObjectNode ++ val emsIp = mountPayloadObject.get("node")?.get(0)?.get("netconf-node-topology:host")?.asText() ++ ++ model.deviceId = "ems-" + emsIp?.replace(".", "-") ++ mountPayload = mountPayload.replace("%ems-id%", model.deviceId) ++ + log.debug("Mount Payload : $mountPayload") + restconfMountDevice(model.client, model.deviceId, mountPayload, mutableMapOf("Content-Type" to "application/json")) + +@@ -70,40 +78,34 @@ + } + } + +- private fun validatedPayload(executionRequest: ExecutionServiceInput): SoftwareUpgradeModel { ++ private fun validatedPayload(executionRequest: ExecutionServiceInput): SoftwareManagementModel { + val properties = requestPayloadActionProperty(executionRequest.actionIdentifiers.actionName + "-properties")!!.get(0) + if(!properties?.get("pnf-id")?.textValue().isNullOrEmpty() && + !properties?.get("target-software-version")?.textValue().isNullOrEmpty()) { +- return SoftwareUpgradeModel(getDynamicProperties("resolution-key").asText(), ++ return SoftwareManagementModel(getDynamicProperties("resolution-key").asText(), + BluePrintDependencyService.restClientService(RESTCONF_SERVER_IDENTIFIER), +- properties.get("pnf-id").textValue(), properties.get("target-software-version").textValue(), ++ "", ++ properties.get("pnf-id").textValue(), ++ properties.get("target-software-version").textValue(), + Action.getEnumFromActionName(executionRequest.actionIdentifiers.actionName)) + }else{ + throw BluePrintException("Invalid parameters sent to CDS. Request parameters pnf-id or target-software-version missing") + } + } + +- private suspend fun processPreCheck(model: SoftwareUpgradeModel) { ++ private suspend fun processPreCheck(model: SoftwareManagementModel) { + 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.debug("Current sw version on pnf : ${payloadObject.get("software-management")?.get("pnf-software-package")?.get(0)?.get("software-version")?.asText()}") + log.info("PNF is Healthy!") + } + +- private suspend fun processDownloadNESw(model: SoftwareUpgradeModel) { ++ private suspend fun processDownloadNESw(model: SoftwareManagementModel) { + 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 +- } ++ var downloadConfigPayload: String = contentFromResolvedArtifactNB("download-ne-sw") ++ model.yangId = model.neIdentifier + downloadConfigPayload = downloadConfigPayload.replace("%actionName%", Action.DOWNLOAD_NE_SW.name) + log.info("Config Payload to start download : $downloadConfigPayload") + +@@ -115,14 +117,15 @@ + checkExecution(model) + } + +- private suspend fun processActivateNESw(model: SoftwareUpgradeModel) { ++ private suspend fun processActivateNESw(model: SoftwareManagementModel) { + 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") ++ var activateConfigPayload: String = contentFromResolvedArtifactNB("activate-ne-sw") + activateConfigPayload = activateConfigPayload.replace("%actionName%", Action.ACTIVATE_NE_SW.name) +- activateConfigPayload = activateConfigPayload.replace("%id%", model.yangId) + 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")) +@@ -134,7 +137,7 @@ + } + } + +- private suspend fun processPostCheck(model: SoftwareUpgradeModel) { ++ private suspend fun processPostCheck(model: SoftwareManagementModel) { + log.info("In PNF SW upgrade : processPostCheck") + //Log the current configuration for the subtree + if (checkIfSwReadyToPerformAction(Action.POST_CHECK, model)) { +@@ -142,35 +145,36 @@ + } + } + +- private fun processCancel(model :SoftwareUpgradeModel) { ++ private fun processCancel(model :SoftwareManagementModel) { + //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{ ++ private suspend fun getCurrentConfig(model: SoftwareManagementModel) : ObjectNode{ + val currentConfig: BlueprintWebClientService.WebClientResponse<String> = restconfDeviceConfig(model.client, model.deviceId, CONFIGLET_RESOURCE_PATH) + return JacksonUtils.jsonNode(currentConfig.body) as ObjectNode + } +- private suspend fun checkExecution(model: SoftwareUpgradeModel) { ++ ++ private suspend fun checkExecution(model: SoftwareManagementModel) { + 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}") ++ throw BluePrintRetryException("Waiting for device(${model.deviceId}) to complete ${model.action.name}") + } + } + model.client.retry<String>(10, 0, 1000, checkExecutionBlock) + + } + +- private suspend fun checkIfSwReadyToPerformAction(action : Action, model: SoftwareUpgradeModel): Boolean { ++ private suspend fun checkIfSwReadyToPerformAction(action : Action, model: SoftwareManagementModel): Boolean { + val configBody = getCurrentConfig(model) +- configBody.get("software-upgrade")?.get("upgrade-package")?.iterator()?.forEach { item -> ++ configBody.get("software-management")?.get("pnf-software-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() ++ model.yangId= item.get("neIdentifier").textValue() + return true + } + } +@@ -201,5 +205,12 @@ + } + } + +-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 ++data class SoftwareManagementModel( ++ val resolutionKey: String, ++ val client: BlueprintWebClientService, ++ var deviceId: String, ++ val neIdentifier: String, ++ val targetSwVersion: String, ++ val action: Action, ++ var yangId: String = "" ++) +\ No newline at end of file diff --git a/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/TOSCA.meta.patch b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/TOSCA.meta.patch new file mode 100644 index 000000000..29ce13c10 --- /dev/null +++ b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/TOSCA.meta.patch @@ -0,0 +1,13 @@ +--- PNF_CDS_RESTCONF/TOSCA-Metadata/TOSCA.meta 2020-03-12 12:13:38.654109400 +0800 ++++ PNF_SW_UPGRADE_WITH_EM/TOSCA-Metadata/TOSCA.meta 2020-03-13 00:05:30.186335900 +0800 +@@ -1,7 +1,7 @@ + TOSCA-Meta-File-Version: 1.0.0 + CSAR-Version: 1.0 + Created-By: Raj Gumma <raj.gumma@est.tech> +-Entry-Definitions: Definitions/PNF_CDS_RESTCONF.json +-Template-Name: PNF_CDS_RESTCONF ++Entry-Definitions: Definitions/PNF_SW_UPGRADE_WITH_EM.json ++Template-Name: PNF_SW_UPGRADE_WITH_EM + Template-Version: 1.0.0 +-Template-Tags: PNF_CDS_RESTCONF ++Template-Tags: PNF_SW_UPGRADE_WITH_EM diff --git a/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/pnf-software-upgrade-mapping.json.patch b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/pnf-software-upgrade-mapping.json.patch new file mode 100644 index 000000000..74c5d184e --- /dev/null +++ b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/pnf-software-upgrade-mapping.json.patch @@ -0,0 +1,17 @@ +--- PNF_CDS_RESTCONF/Definitions/pnf-software-upgrade-mapping.json 2020-03-12 12:13:38.642390600 +0800 ++++ PNF_SW_UPGRADE_WITH_EM/Definitions/pnf-software-upgrade-mapping.json 2020-03-12 23:59:44.111140600 +0800 +@@ -22,12 +22,12 @@ + ] + }, + { +- "name": "pnf-ipv4-address", ++ "name": "ems-ipv4-address", + "input-param": false, + "property": { + "type": "string" + }, +- "dictionary-name": "pnf-ipaddress-aai", ++ "dictionary-name": "ems-ipaddress-aai", + "dictionary-source": "aai-data", + "dependencies": [ + "pnf-id" diff --git a/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/pnf-swug-activate-ne-sw-template.vtl.patch b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/pnf-swug-activate-ne-sw-template.vtl.patch new file mode 100644 index 000000000..7d8d7efe7 --- /dev/null +++ b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/pnf-swug-activate-ne-sw-template.vtl.patch @@ -0,0 +1,19 @@ +--- PNF_CDS_RESTCONF/Templates/pnf-swug-config-template.vtl 2020-03-12 19:06:30.108210900 +0800 ++++ PNF_SW_UPGRADE_WITH_EM/Templates/pnf-swug-activate-ne-sw-template.vtl 2020-03-12 23:29:51.565242200 +0800 +@@ -7,11 +7,12 @@ + "operation": "merge", + "target": "/", + "value": { +- "software-upgrade": { +- "upgrade-package": [ ++ "software-management": { ++ "pnf-software-package": [ + { +- "id": "%id%", +- "action": "%actionName%" ++ "neIdentifier": "${pnf-id}", ++ "action": "%actionName%", ++ "swVersionToBeActivated": "${target-software-version}" + } + ] + } diff --git a/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/pnf-swug-download-ne-sw-template.vtl.patch b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/pnf-swug-download-ne-sw-template.vtl.patch new file mode 100644 index 000000000..f74ad838a --- /dev/null +++ b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/pnf-swug-download-ne-sw-template.vtl.patch @@ -0,0 +1,34 @@ +--- PNF_CDS_RESTCONF/Templates/pnf-swug-download-ne-sw-template.vtl 2020-03-12 12:13:38.660945300 +0800 ++++ PNF_SW_UPGRADE_WITH_EM/Templates/pnf-swug-download-ne-sw-template.vtl 2020-03-12 23:12:49.980281200 +0800 +@@ -7,17 +7,21 @@ + "operation": "merge", + "target": "/", + "value": { +- "software-upgrade": { +- "upgrade-package": [ ++ "software-management": { ++ "pnf-software-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" ++ "neIdentifier": "${pnf-id}", ++ "current-status": "INITIALIZED", ++ "action": "%actionName%", ++ "software-version": "${target-software-version}", ++ "swToBeDownloaded": [ ++ { ++ "swLocation": "http://192.168.35.96:10080/ran_du_pkg1-v2.zip", ++ "swFileSize": "12345678", ++ "swFileCompression": "ZIP", ++ "swFileFormat": "binary" ++ } ++ ] + } + ] + } diff --git a/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/resources_definition_types.json.patch b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/resources_definition_types.json.patch new file mode 100644 index 000000000..74f3fb0fb --- /dev/null +++ b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/resources_definition_types.json.patch @@ -0,0 +1,14 @@ +--- PNF_CDS_RESTCONF/Definitions/resources_definition_types.json 2020-03-12 12:13:38.643367200 +0800 ++++ PNF_SW_UPGRADE_WITH_EM/Definitions/resources_definition_types.json 2020-03-12 23:55:20.636531200 +0800 +@@ -14,9 +14,9 @@ + } + } + }, +- "pnf-ipaddress-aai" : { ++ "ems-ipaddress-aai" : { + "tags" : "aai-get", +- "name" : "pnf-ipaddress-aai", ++ "name" : "ems-ipaddress-aai", + "property" : { + "description" : "primary aai data to get resource", + "type" : "string" diff --git a/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/restconf-mount-template.vtl.patch b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/restconf-mount-template.vtl.patch new file mode 100644 index 000000000..b5a9c5bb2 --- /dev/null +++ b/test/mocks/emssimulator/pnf-sw-upgrade-cba-builder/patches/restconf-mount-template.vtl.patch @@ -0,0 +1,16 @@ +--- PNF_CDS_RESTCONF/Templates/restconf-mount-template.vtl 2020-03-12 12:13:38.664851500 +0800 ++++ PNF_SW_UPGRADE_WITH_EM/Templates/restconf-mount-template.vtl 2020-03-12 23:36:52.209773400 +0800 +@@ -1,11 +1,11 @@ + { + "node": [ + { +- "node-id": "${pnf-id}", ++ "node-id": "%ems-id%", + "netconf-node-topology:protocol": { + "name": "TLS" + }, +- "netconf-node-topology:host": "${pnf-ipv4-address}", ++ "netconf-node-topology:host": "${ems-ipv4-address}", + "netconf-node-topology:key-based": { + "username": "netconf", + "key-id": "ODL_private_key_0" diff --git a/test/mocks/emssimulator/swm-netconf/docker-compose.yml b/test/mocks/emssimulator/swm-netconf/docker-compose.yml new file mode 100644 index 000000000..b72668566 --- /dev/null +++ b/test/mocks/emssimulator/swm-netconf/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3' + +services: + ems-netconf-swm: + image: nexus3.onap.org:10001/onap/integration/simulators/netconf-pnp-simulator:2.8.2 + #image: netconf-pnp-simulator:latest + container_name: ems-netconf-swm + restart: always + ports: + - "830:830" + - "6513:6513" + volumes: + - ./pnf-swm:/config/modules/pnf-swm/ diff --git a/test/mocks/emssimulator/swm-netconf/pnf-swm/LICENSE b/test/mocks/emssimulator/swm-netconf/pnf-swm/LICENSE new file mode 100644 index 000000000..3eface2c7 --- /dev/null +++ b/test/mocks/emssimulator/swm-netconf/pnf-swm/LICENSE @@ -0,0 +1,13 @@ +Copyright (C) 2019 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. diff --git a/test/mocks/emssimulator/swm-netconf/pnf-swm/LICENSE-2 b/test/mocks/emssimulator/swm-netconf/pnf-swm/LICENSE-2 new file mode 100644 index 000000000..24d86c289 --- /dev/null +++ b/test/mocks/emssimulator/swm-netconf/pnf-swm/LICENSE-2 @@ -0,0 +1,16 @@ +/*- + * ================================================================================ + * Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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. + */ diff --git a/test/mocks/emssimulator/swm-netconf/pnf-swm/README b/test/mocks/emssimulator/swm-netconf/pnf-swm/README new file mode 100644 index 000000000..303511647 --- /dev/null +++ b/test/mocks/emssimulator/swm-netconf/pnf-swm/README @@ -0,0 +1,9 @@ +1. Introduction + The directory /pnf-swm shall be located in ems-netconf-swm container for integrtion test for PNF software upgrade with EM with Netconf. + +2. Directory structure + a. model.yang: YANG model for donwnloadNESw and activateNESw operations of software management. + b. data.xml: Initial data or device configuration info stored in ems-netconf-swm container. + c. subscribe.py: Inherited from /integration/test/mocks/netconf-pnp-simulator/modules/pnf-sw/upgrade/ and mofication for the model.yang + d. LICENSE: original license from /integration/test/mocks/netconf-pnp-simulator/modules/pnf-sw/upgrade/ + e. LICENSE-2: new license info for enhancement diff --git a/test/mocks/emssimulator/swm-netconf/pnf-swm/data.xml b/test/mocks/emssimulator/swm-netconf/pnf-swm/data.xml new file mode 100644 index 000000000..3ef512550 --- /dev/null +++ b/test/mocks/emssimulator/swm-netconf/pnf-swm/data.xml @@ -0,0 +1,13 @@ +<?xml version='1.0' encoding='UTF-8'?> +<software-management xmlns="http://onap.org/pnf-swm"> + <pnf-software-package> + <neIdentifier>5gDU0001</neIdentifier> + <current-status>CREATED</current-status> + <software-version>v1</software-version> + </pnf-software-package> + <pnf-software-package> + <neIdentifier>5gDU0002</neIdentifier> + <current-status>CREATED</current-status> + <software-version>v1</software-version> + </pnf-software-package> +</software-management> diff --git a/test/mocks/emssimulator/swm-netconf/pnf-swm/model.yang b/test/mocks/emssimulator/swm-netconf/pnf-swm/model.yang new file mode 100644 index 000000000..de1daefac --- /dev/null +++ b/test/mocks/emssimulator/swm-netconf/pnf-swm/model.yang @@ -0,0 +1,86 @@ +module pnf-swm { + namespace "http://onap.org/pnf-swm"; + prefix swm; + + import ietf-yang-types { + prefix yang; + } + + revision "2020-03-10" { + description + "initial version, Ref. 3GPP 32.532-f00"; + } + + container software-management { + config true; + list pnf-software-package { + key "neIdentifier"; + leaf neIdentifier { + type string; + description + "NE identifier."; + } + leaf current-status { + type enumeration { + enum CREATED; + enum INITIALIZED; + enum DOWNLOAD_IN_PROGRESS; + enum DOWNLOAD_COMPLETED; + enum ACTIVATION_IN_PROGRESS; + enum ACTIVATION_COMPLETED; + } + description + "List of possible states of the upgrade"; + } + leaf state-change-time { + mandatory false; + description + "Date and time of the last state change."; + type yang:date-and-time; + } + leaf action { + mandatory false; + type enumeration { + enum NONE; + enum PRE_CHECK; + enum DOWNLOAD_NE_SW; + enum ACTIVATE_NE_SW; + enum POST_CHECK; + enum CANCEL; + } + description + "List of possible actions for the upgrade"; + } + leaf software-version { + type string; + description + "Possible name or release version of the UP"; + } + list swToBeDownloaded { + key "swLocation"; + leaf swLocation { + type string; + description + "Software location to be downloaded."; + } + leaf swFileSize { + type uint64; + description "Software file size."; + } + leaf swFileCompression { + type string; + description "Software file compression algorithm."; + } + leaf swFileFormat { + type string; + description "Software file format."; + } + } + leaf swVersionToBeActivated { + type string; + description + "Software version to be activated."; + } + } + } +} diff --git a/test/mocks/emssimulator/swm-netconf/pnf-swm/subscriber.py b/test/mocks/emssimulator/swm-netconf/pnf-swm/subscriber.py new file mode 100755 index 000000000..56d061906 --- /dev/null +++ b/test/mocks/emssimulator/swm-netconf/pnf-swm/subscriber.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 + +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Nordix Foundation. +# ================================================================================ +# Modification Copyright 2020 Huawei Technologies Co., Ltd +# ================================================================================ +# 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========================================================= + +__author__ = "Eliezio Oliveira <eliezio.oliveira@est.tech>" +__copyright__ = "Copyright (C) 2020 Nordix Foundation, and Huawei" +__license__ = "Apache 2.0" + +import time +from concurrent.futures import ThreadPoolExecutor +from threading import Timer + +import sysrepo as sr + +YANG_MODULE_NAME = 'pnf-swm' + +# +# ----- BEGIN Finite State Machine definitions ----- +# + +# Actions +ACT_PRE_CHECK = 'PRE_CHECK' +ACT_DOWNLOAD_NE_SW = 'DOWNLOAD_NE_SW' +ACT_ACTIVATE_NE_SW = 'ACTIVATE_NE_SW' +ACT_CANCEL = 'CANCEL' + +# States +ST_CREATED = 'CREATED' +ST_INITIALIZED = 'INITIALIZED' +ST_DOWNLOAD_IN_PROGRESS = 'DOWNLOAD_IN_PROGRESS' +ST_DOWNLOAD_COMPLETED = 'DOWNLOAD_COMPLETED' +ST_ACTIVATION_IN_PROGRESS = 'ACTIVATION_IN_PROGRESS' +ST_ACTIVATION_COMPLETED = 'ACTIVATION_COMPLETED' + +# Timeout used for timed transitions +TO_DOWNLOAD = 7 +TO_ACTIVATION = 7 + + +def timestamper(sess, key_id): + xpath = xpath_of(key_id, 'state-change-time') + now = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) + state = sr.Val(now, sr.SR_STRING_T) + sess.set_item(xpath, state) + + +def xpath_of(key_id, leaf_id): + selector = "[neIdentifier='{0}']".format(key_id) if key_id else '' + return "/%s:software-management/pnf-software-package%s/%s" % (YANG_MODULE_NAME, selector, leaf_id) + + +""" +The finite state machine (FSM) is represented as a dictionary where the current state is the key, and its value is +an object (also represented as a dictionary) with the following optional attributes: + +- on_enter: a function called when FSM enters this state; +- transitions: a dictionary mapping every acceptable action to the target state; +- timed_transition: a pair for a timed transition that will automatically occur after a given interval. +""" +STATE_MACHINE = { + ST_CREATED: { + 'transitions': {ACT_PRE_CHECK: ST_INITIALIZED} + }, + ST_INITIALIZED: { + 'on_enter': timestamper, + 'transitions': {ACT_DOWNLOAD_NE_SW: ST_DOWNLOAD_IN_PROGRESS} + }, + ST_DOWNLOAD_IN_PROGRESS: { + 'on_enter': timestamper, + 'timed_transition': (TO_DOWNLOAD, ST_DOWNLOAD_COMPLETED), + 'transitions': {ACT_CANCEL: ST_INITIALIZED} + }, + ST_DOWNLOAD_COMPLETED: { + 'on_enter': timestamper, + 'transitions': {ACT_ACTIVATE_NE_SW: ST_ACTIVATION_IN_PROGRESS} + }, + ST_ACTIVATION_IN_PROGRESS: { + 'on_enter': timestamper, + 'timed_transition': (TO_ACTIVATION, ST_ACTIVATION_COMPLETED), + 'transitions': {ACT_CANCEL: ST_DOWNLOAD_COMPLETED} + }, + ST_ACTIVATION_COMPLETED: { + 'on_enter': timestamper, + 'transitions': {ACT_ACTIVATE_NE_SW: ST_ACTIVATION_IN_PROGRESS} + } +} + +# +# ----- END Finite State Machine definitions ----- +# + + +def main(): + try: + conn = sr.Connection(YANG_MODULE_NAME) + sess = sr.Session(conn) + subscribe = sr.Subscribe(sess) + + subscribe.module_change_subscribe(YANG_MODULE_NAME, module_change_cb, conn) + + try: + print_current_config(sess, YANG_MODULE_NAME) + except Exception as e: + print(e) + + sr.global_loop() + + print("Application exit requested, exiting.") + except Exception as e: + print(e) + + +# Function to be called for subscribed client of given session whenever configuration changes. +def module_change_cb(sess, module_name, event, private_ctx): + try: + conn = private_ctx + change_path = xpath_of(None, 'action') + it = sess.get_changes_iter(change_path) + while True: + change = sess.get_change_next(it) + if change is None: + break + handle_change(conn, change.oper(), change.old_val(), change.new_val()) + except Exception as e: + print(e) + return sr.SR_ERR_OK + + +# Function to print current configuration state. +# It does so by loading all the items of a session and printing them out. +def print_current_config(session, module_name): + select_xpath = "/" + module_name + ":*//*" + + values = session.get_items(select_xpath) + + if values is not None: + print("========== BEGIN CONFIG ==========") + for i in range(values.val_cnt()): + print(values.val(i).to_string(), end='') + print("=========== END CONFIG ===========") + + +def handle_change(conn, op, old_val, new_val): + """ + Handle individual changes on the model. + """ + if op == sr.SR_OP_CREATED: + print("CREATED: %s" % new_val.to_string()) + xpath = new_val.xpath() + last_node = xpath_ctx.last_node(xpath) + # Warning: 'key_value' modifies 'xpath'! + key_id = xpath_ctx.key_value(xpath, 'pnf-software-package', 'neIdentifier') + if key_id and last_node == 'action': + executor.submit(execute_action, conn, key_id, new_val.data().get_enum()) + elif op == sr.SR_OP_DELETED: + print("DELETED: %s" % old_val.to_string()) + elif op == sr.SR_OP_MODIFIED: + print("MODIFIED: %s to %s" % (old_val.to_string(), new_val.to_string())) + elif op == sr.SR_OP_MOVED: + print("MOVED: %s after %s" % (new_val.xpath(), old_val.xpath())) + + +def execute_action(conn, key_id, action): + sess = sr.Session(conn) + try: + cur_state = sess.get_item(xpath_of(key_id, 'current-status')).data().get_enum() + next_state_str = STATE_MACHINE[cur_state]['transitions'].get(action, None) + if next_state_str: + handle_set_state(conn, key_id, next_state_str) + sess.delete_item(xpath_of(key_id, 'action')) + sess.commit() + finally: + sess.session_stop() + + +def handle_set_state(conn, key_id, state_str): + sess = sr.Session(conn) + try: + state = sr.Val(state_str, sr.SR_ENUM_T) + sess.set_item(xpath_of(key_id, 'current-status'), state) + on_enter = STATE_MACHINE[state_str].get('on_enter', None) + if on_enter: + # noinspection PyCallingNonCallable + on_enter(sess, key_id) + sess.commit() + delay, next_state_str = STATE_MACHINE[state_str].get('timed_transition', [0, None]) + if delay: + Timer(delay, handle_set_state, (conn, key_id, next_state_str)).start() + finally: + sess.session_stop() + + +if __name__ == '__main__': + xpath_ctx = sr.Xpath_Ctx() + executor = ThreadPoolExecutor(max_workers=2) + main() diff --git a/test/mocks/emssimulator/swm/activateNESw b/test/mocks/emssimulator/swm/activateNESw index 67d233e24..3531f1634 100755 --- a/test/mocks/emssimulator/swm/activateNESw +++ b/test/mocks/emssimulator/swm/activateNESw @@ -1,117 +1,28 @@ -#!/usr/bin/python +#!/usr/bin/python3 +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 sys import argparse import json -import os -import shutil -import random -import time import conf -import ems_util - - -def do_activate_sw(sw_version_to_be_activated, ne_info): - """ - return err, reason - """ - - installed_sw = ne_info.get("installedSw", {}) - if sw_version_to_be_activated in installed_sw: - target_sw_version = installed_sw[sw_version_to_be_activated]["version"] - else: - target_sw_version = sw_version_to_be_activated - - sw_install_dir_in_ne = conf.PNF_SIMULATORS_DIR + '/' + ne_info['omIP'] + conf.PNF_SW_INSTALL_DIR - target_sw_dir = sw_install_dir_in_ne + '/' + target_sw_version - if not os.path.isdir(target_sw_dir): - return True, "SW to be activated does not install" - - if "targetSwVersion" in ne_info: - if ne_info["targetSwVersion"] != target_sw_version: - return True, "Conflicted targetVersion with to be activated %s" % target_sw_version - del ne_info["targetSwVersion"] - - old_sw_version = ne_info.get("oldSwVersion", "") - - if target_sw_version != ne_info["currentSwVersion"]: - ne_info["oldSwVersion"] = ne_info["currentSwVersion"] - ne_info["currentSwVersion"] = target_sw_version - ne_info["status"] = conf.STATUS_ACTIVATING - ems_util.update_ne_info(ne_info) - - if target_sw_version != old_sw_version: - old_sw_dir = sw_install_dir_in_ne + '/' + old_sw_version - if old_sw_version and os.path.isdir(old_sw_dir): - shutil.rmtree(old_sw_dir, ignore_errors=True) - - old_cwd = os.getcwd() - os.chdir(sw_install_dir_in_ne) - if os.path.islink(conf.CURRENT_VERSION_DIR): - os.remove(conf.CURRENT_VERSION_DIR) - os.symlink(target_sw_version, conf.CURRENT_VERSION_DIR) - os.chdir(old_cwd) - - if "downloadedSwLocation" in ne_info: - if os.path.isdir(ne_info["downloadedSwLocation"]): - shutil.rmtree(ne_info["downloadedSwLocation"], ignore_errors=True) - del ne_info["downloadedSwLocation"] - - return False, None - - -def generate_notification(activate_process_id, activate_status, sw_version, failure_reason): - notification = { - "objectClass": "EMSClass", - "objectInstance": "EMSInstance", - "notificationId": random.randint(1, conf.MAX_INT), - "eventTime": time.asctime(), - "systemDN": "emssimulator", - "notificationType": "notifyActivateNESwStatusChanged", - "activateProcessId": activate_process_id, - "activateOperationStatus": activate_status, - "swVersion": sw_version - } - - if failure_reason: - notification["failureReason"] = failure_reason - - return notification - - -def activate_ne_sw(sw_version_to_be_activated, ne_id): - ne_info = ems_util.get_ne_info_from_db_by_id(ne_id) - - activate_process_id = random.randint(1, conf.MAX_INT) - result = conf.REQ_SUCCESS - ret_value = { - "activateProcessId": activate_process_id, - "result": result - } - - if not ne_info: - ret_value["result"] = conf.REQ_FAILURE - ret_value["reason"] = "Can not find NE %s" % ne_id - return ret_value - - err, reason = do_activate_sw(sw_version_to_be_activated, ne_info) - - if not err: - ne_info["status"] = conf.STATUS_ACTIVATED - ems_util.update_ne_info(ne_info) - activate_status = "NE_SWACTIVATION_SUCCESSFUL" - else: - ret_value["result"] = conf.REQ_FAILURE - ret_value["reason"] = reason - - activate_status = "NE_SWACTIVATION_FAILED" - - notification = generate_notification(activate_process_id, activate_status, sw_version_to_be_activated, reason) - ems_util.send_notification(notification, activate_process_id) - - # for automated software management, there is no listOfStepNumbersAndDurations - return ret_value +import activate_n_e_sw def main(): @@ -122,8 +33,8 @@ def main(): args = parser.parse_args() - ret_value = activate_ne_sw(args.swVersionToBeActivated, args.neIdentifier) - print json.dumps(ret_value) + _, ret_value = activate_n_e_sw.activate(args.swVersionToBeActivated, args.neIdentifier) + print(json.dumps(ret_value)) if ret_value["result"] == conf.REQ_SUCCESS: sys.exit(conf.RET_CODE_SUCCESS) diff --git a/test/mocks/emssimulator/swm/activate_n_e_sw.py b/test/mocks/emssimulator/swm/activate_n_e_sw.py new file mode 100644 index 000000000..26214fa53 --- /dev/null +++ b/test/mocks/emssimulator/swm/activate_n_e_sw.py @@ -0,0 +1,141 @@ +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 os +import logging +import shutil +import random +import time + +import conf +import ems_util + + +OPERATION_NAME = "activateNESw" +logging.basicConfig(level=logging.INFO, format=conf.LOGGER_FORMAT, filename=ems_util.get_log_file(OPERATION_NAME)) +logger = logging.getLogger(OPERATION_NAME) + + +def do_activate(sw_version_to_be_activated, ne_info): + """ + return err, reason + """ + + logger.info("swVersionToBeActivated: %s" % sw_version_to_be_activated) + + installed_sw = ne_info.get("installedSw", {}) + if sw_version_to_be_activated in installed_sw: + target_sw_version = installed_sw[sw_version_to_be_activated]["version"] + else: + target_sw_version = sw_version_to_be_activated + + sw_install_dir_in_ne = ems_util.get_install_dir(ne_info['omIP']) + logger.info("SW has been installed at %s" % sw_install_dir_in_ne) + + if "targetSwVersion" in ne_info: + if ne_info["targetSwVersion"] != target_sw_version: + msg = "Conflicted targetVersion with to be activated %s" % target_sw_version + logger.error(msg) + return True, msg + del ne_info["targetSwVersion"] + + old_sw_version = ne_info.get("oldSwVersion", "") + + if target_sw_version != ne_info["currentSwVersion"]: + ne_info["oldSwVersion"] = ne_info["currentSwVersion"] + ne_info["currentSwVersion"] = target_sw_version + ne_info["status"] = conf.STATUS_ACTIVATING + ems_util.update_ne_info(ne_info) + + if target_sw_version != old_sw_version: + old_sw_dir = os.path.join(sw_install_dir_in_ne, old_sw_version) + if old_sw_version and os.path.isdir(old_sw_dir): + shutil.rmtree(old_sw_dir, ignore_errors=True) + + old_cwd = os.getcwd() + os.chdir(sw_install_dir_in_ne) + if os.path.islink(conf.CURRENT_VERSION_DIR): + os.remove(conf.CURRENT_VERSION_DIR) + os.symlink(target_sw_version, conf.CURRENT_VERSION_DIR) + os.chdir(old_cwd) + + if "downloadedSwLocation" in ne_info: + if os.path.isdir(ne_info["downloadedSwLocation"]): + shutil.rmtree(ne_info["downloadedSwLocation"], ignore_errors=True) + del ne_info["downloadedSwLocation"] + + return False, None + + +def generate_notification(activate_process_id, activate_status, sw_version, failure_reason): + notification = { + "objectClass": conf.OBJECT_CLASS, + "objectInstance": conf.OBJECT_INSTANCE, + "notificationId": random.randint(1, conf.MAX_INT), + "eventTime": time.asctime(), + "systemDN": conf.SYSTEM_DN, + "notificationType": "notifyActivateNESwStatusChanged", + "activateProcessId": activate_process_id, + "activateOperationStatus": activate_status, + "swVersion": sw_version + } + + if failure_reason: + logger.error(failure_reason) + notification["failureReason"] = failure_reason + + return notification + + +def activate(sw_version_to_be_activated, ne_id): + ne_info = ems_util.get_ne_info_from_db_by_id(ne_id) + + activate_process_id = random.randint(1, conf.MAX_INT) + result = conf.REQ_SUCCESS + ret_value = { + "activateProcessId": activate_process_id, + "result": result + } + + if not ne_info: + ret_value["result"] = conf.REQ_FAILURE + ret_value["reason"] = "Can not find NE %s" % ne_id + + logger.error(ret_value["reason"]) + return ret_value + + err, reason = do_activate(sw_version_to_be_activated, ne_info) + + if not err: + ne_info["status"] = conf.STATUS_ACTIVATED + ems_util.update_ne_info(ne_info) + + logger.info("Activate SW success") + activate_status = "NE_SWACTIVATION_SUCCESSFUL" + else: + ret_value["result"] = conf.REQ_FAILURE + ret_value["reason"] = reason + + logger.error("Activate SW failure, reason: %s" % ret_value["reason"]) + activate_status = "NE_SWACTIVATION_FAILED" + + notification = generate_notification(activate_process_id, activate_status, sw_version_to_be_activated, reason) + ems_util.send_notification(notification, activate_process_id) + + # for automated software management, there is no listOfStepNumbersAndDurations + return notification, ret_value diff --git a/test/mocks/emssimulator/swm/conf.py b/test/mocks/emssimulator/swm/conf.py index d7ba5b4d5..5aac383d9 100644 --- a/test/mocks/emssimulator/swm/conf.py +++ b/test/mocks/emssimulator/swm/conf.py @@ -1,22 +1,46 @@ -#!/usr/bin/python +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 sys +import os SWM_DIR = sys.path[0] -NE_INFO_TABLE = SWM_DIR + "/ems_db/ne_info_table.json" -SW_SERVER_SIMULATOR = SWM_DIR + "/sw_server_simulator" -PNF_SIMULATORS_DIR = SWM_DIR + "/pnf_simulators" -PNF_SW_DOWNLOAD_DIR = "/opt/download" -PNF_SW_INSTALL_DIR = "/opt/install" -PNF_SW_FALLBACK_DIR = "/opt/fallback" +LOGGER_FORMAT = "[%(asctime)-15s] %(levelname)s [%(name)s]: %(message)s" +LOGGER_FILE_DIR = os.path.join(SWM_DIR, "log") + +NE_INFO_TABLE = os.path.join(SWM_DIR, "ems_db", "ne_info_table.json") +SW_SERVER_SIMULATOR = os.path.join(SWM_DIR, "sw_server_simulator") +PNF_SIMULATORS_DIR = os.path.join(SWM_DIR, "pnf_simulators") +COMMON_PATH = "opt" +PNF_SW_DOWNLOAD_DIR = "download" +PNF_SW_INSTALL_DIR = "install" MANIFEST_FILE = "manifest.json" -INSTALLED_SW = "installed_sw.json" +INSTALLED_SW_FILE = "installed_sw.json" CURRENT_VERSION_DIR = "current" NOTIFICATION_DIR = "/tmp" MAX_INT = (2**32) - 1 +OBJECT_CLASS = "NRCellDU" +OBJECT_INSTANCE = "DC=com, SubNetwork=1, ManagedElement=123, GNBDUFunction=1, NRCellDU=1" +SYSTEM_DN = "DC=com, SubNetwork=1, ManagedElement=123" + STATUS_DOWNLOADING = "Downloading" STATUS_INSTALLING = "Installing" STATUS_ACTIVATING = "Activating" @@ -34,4 +58,3 @@ RET_CODE_FAILURE = 1 RESULT_SUCCESS = "Success" RESULT_FAILURE = "Failure" RESULT_PARTLY = "Partly successful" - diff --git a/test/mocks/emssimulator/swm/downloadNESw b/test/mocks/emssimulator/swm/downloadNESw index 06a8d6b37..90794488d 100755 --- a/test/mocks/emssimulator/swm/downloadNESw +++ b/test/mocks/emssimulator/swm/downloadNESw @@ -1,150 +1,70 @@ -#!/usr/bin/python +#!/usr/bin/python3 +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 sys import argparse import json -import os -import shutil -import random -import time -import tempfile import conf -import ems_util +import download_n_e_sw +import install_n_e_sw -def do_download_sw(sw_info, sw_download_dir): - """ - return err, reason, file_location_in_nes - """ - - sw_location = sw_info['swLocation'] - - # Use copy file from SW_SERVER_SIMULATOR to simulate download file - sw_file_name = sw_location.split('/')[-1] - - file_location_in_server = conf.SW_SERVER_SIMULATOR + '/' + sw_file_name - file_location_in_ne = sw_download_dir + '/' + sw_file_name - - try: - shutil.copy(file_location_in_server, sw_download_dir) - except IOError as e: - return True, "Download %s to %s error: %s" % (sw_file_name, sw_download_dir, str(e)), file_location_in_ne - - return False, None, file_location_in_ne - - -def generate_notification(download_process_id, download_status, downloaded_ne_sw_info, failed_sw_info): - notification = { - "objectClass": "EMSClass", - "objectInstance": "EMSInstance", - "notificationId": random.randint(1, conf.MAX_INT), - "eventTime": time.asctime(), - "systemDN": "emssimulator", - "notificationType": "notifyDownloadNESwStatusChanged", - "downloadProcessId": download_process_id, - "downloadOperationStatus": download_status - } +def main(): + parser = argparse.ArgumentParser() - if downloaded_ne_sw_info: - notification["downloadedNESwInfo"] = downloaded_ne_sw_info + parser.add_argument("--swToBeDownloaded", help="The NE software to be downloaded", required=True) + parser.add_argument("--neIdentifier", help="The NE where the software can be downloaded", required=True) - if failed_sw_info: - notification["failedSwInfo"] = failed_sw_info + args = parser.parse_args() - return notification + sw_to_be_downloaded = json.loads(args.swToBeDownloaded) + all_installed_ne_sw_list = [] + all_failed_sw_info = [] -def download_ne_sw(sw_to_be_downloaded, ne_id): - ne_info = ems_util.get_ne_info_from_db_by_id(ne_id) + download_notification, download_ret_value = download_n_e_sw.download(sw_to_be_downloaded, args.neIdentifier) - download_process_id = random.randint(1, conf.MAX_INT) - result = conf.REQ_SUCCESS - ret_value = { - "downloadProcessId": download_process_id, - "result": result - } + downloaded_ne_sw_list = download_notification.get("downloadedNESwInfo", []) + failed_downloaded_sw_list = download_notification.get("failedSwInfo", []) + all_failed_sw_info.extend(failed_downloaded_sw_list) - if not ne_info: - ret_value["result"] = conf.REQ_FAILURE - ret_value["reason"] = "Can not find NE %s" % ne_id - return ret_value + for downloaded_ne_sw in downloaded_ne_sw_list: + install_notification, _ = install_n_e_sw.install(downloaded_ne_sw, args.neIdentifier) + installed_ne_sw_list = install_notification.get("installedNESwInfo", []) + failed_installed_sw_list = install_notification.get("failedSwInfo", []) - ne_info["status"] = conf.STATUS_DOWNLOADING - ems_util.update_ne_info(ne_info) + all_installed_ne_sw_list.extend(installed_ne_sw_list) + all_failed_sw_info.extend(failed_installed_sw_list) + num_all_installed_ne_sw_list = len(all_installed_ne_sw_list) num_sw_to_be_downloaded = len(sw_to_be_downloaded) - downloaded_ne_sw_info = [] - failed_sw_info = [] - - sw_download_parent_dir = conf.PNF_SIMULATORS_DIR + '/' + ne_info['omIP'] + conf.PNF_SW_DOWNLOAD_DIR - sw_download_dir = ne_info.get("downloadedSwLocation", "") - try: - if not os.path.isdir(sw_download_parent_dir): - os.makedirs(sw_download_parent_dir) - - if sw_download_dir and not os.path.isdir(sw_download_dir): - os.makedirs(sw_download_dir) - except OSError as e: - ret_value["result"] = conf.REQ_FAILURE - ret_value["reason"] = str(e) - return ret_value - - if not sw_download_dir: - sw_download_dir = tempfile.mkdtemp(dir=sw_download_parent_dir) - - for sw_info in sw_to_be_downloaded: - err, reason, file_location = do_download_sw(sw_info, sw_download_dir) - if not err: - downloaded_ne_sw_info.append(file_location) - else: - result = conf.REQ_FAILURE - failed_sw_entry = { - "failedSw": file_location, - "failureReason": reason - } - failed_sw_info.append(failed_sw_entry) - - num_downloaded_ne_sw = len(downloaded_ne_sw_info) - - if num_downloaded_ne_sw == num_sw_to_be_downloaded: - download_status = "NE_SWDOWNLOAD_SUCCESSFUL" - elif num_downloaded_ne_sw == 0: - download_status = "NE_SWDOWNLOAD_FAILED" + if num_all_installed_ne_sw_list == num_sw_to_be_downloaded: + download_ret_value["result"] = conf.REQ_SUCCESS else: - download_status = "NE_SWDOWNLOAD_PARTIALLY_SUCCESSFUL" - - notification = generate_notification(download_process_id, download_status, downloaded_ne_sw_info, failed_sw_info) - ems_util.send_notification(notification, download_process_id) - - if result == conf.REQ_SUCCESS: - ne_info["downloadedSwLocation"] = sw_download_dir - ems_util.update_ne_info(ne_info) - else: - shutil.rmtree(sw_download_dir, ignore_errors=True) - - ret_value["result"] = result - ret_value["reason"] = json.dumps(failed_sw_info) - - # for automated software management, there is no listOfStepNumbersAndDurations - return ret_value - - -def main(): - parser = argparse.ArgumentParser() - - parser.add_argument("--swToBeDownloaded", help="The NE software to be downloaded", required=True) - parser.add_argument("--neIdentifier", help="The NE where the software can be downloaded", required=True) - - args = parser.parse_args() - - sw_to_be_downloaded = json.loads(args.swToBeDownloaded) + download_ret_value["result"] = conf.REQ_FAILURE + download_ret_value["reason"] = "Failed downloaded or installed SW: %s" % json.dumps(all_failed_sw_info) - ret_value = download_ne_sw(sw_to_be_downloaded, args.neIdentifier) - print json.dumps(ret_value) + print(json.dumps(download_ret_value)) - if ret_value["result"] == conf.REQ_SUCCESS: + if download_ret_value["result"] == conf.REQ_SUCCESS: sys.exit(conf.RET_CODE_SUCCESS) else: sys.exit(conf.RET_CODE_FAILURE) diff --git a/test/mocks/emssimulator/swm/download_n_e_sw.py b/test/mocks/emssimulator/swm/download_n_e_sw.py new file mode 100644 index 000000000..05ae4bb6a --- /dev/null +++ b/test/mocks/emssimulator/swm/download_n_e_sw.py @@ -0,0 +1,169 @@ +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 os +import logging +import json +import shutil +import random +import time +import tempfile + +import conf +import ems_util + + +OPERATION_NAME = "downloadNESw" +logging.basicConfig(level=logging.INFO, format=conf.LOGGER_FORMAT, filename=ems_util.get_log_file(OPERATION_NAME)) +logger = logging.getLogger(OPERATION_NAME) + + +def do_download(sw_info, sw_download_dir): + """ + return err, reason, file_location_in_nes + """ + + sw_location = sw_info['swLocation'] + + logger.info("swLocationToBeDownloaded: %s" % sw_location) + + # Use copy file from SW_SERVER_SIMULATOR to simulate download file + sw_file_name = sw_location.split('/')[-1] + + file_location_in_server = os.path.join(conf.SW_SERVER_SIMULATOR, sw_file_name) + file_location_in_ne = os.path.join(sw_download_dir, sw_file_name) + + try: + shutil.copy(file_location_in_server, sw_download_dir) + except IOError as e: + msg = "Download %s to %s error: %s" % (sw_file_name, sw_download_dir, str(e)) + logger.error(msg) + return True, msg, file_location_in_ne + + return False, None, file_location_in_ne + + +def generate_notification(download_process_id, download_status, downloaded_ne_sw_info, failed_sw_info): + notification = { + "objectClass": conf.OBJECT_CLASS, + "objectInstance": conf.OBJECT_INSTANCE, + "notificationId": random.randint(1, conf.MAX_INT), + "eventTime": time.asctime(), + "systemDN": conf.SYSTEM_DN, + "notificationType": "notifyDownloadNESwStatusChanged", + "downloadProcessId": download_process_id, + "downloadOperationStatus": download_status + } + + if downloaded_ne_sw_info: + notification["downloadedNESwInfo"] = downloaded_ne_sw_info + + if failed_sw_info: + notification["failedSwInfo"] = failed_sw_info + + return notification + + +def download(sw_to_be_downloaded, ne_id): + ne_info = ems_util.get_ne_info_from_db_by_id(ne_id) + + download_process_id = random.randint(1, conf.MAX_INT) + result = conf.REQ_SUCCESS + ret_value = { + "downloadProcessId": download_process_id, + "result": result + } + + if not ne_info: + ret_value["result"] = conf.REQ_FAILURE + ret_value["reason"] = "Can not find NE %s" % ne_id + + logger.error(ret_value["reason"]) + return ret_value + + ne_info["status"] = conf.STATUS_DOWNLOADING + ems_util.update_ne_info(ne_info) + + num_sw_to_be_downloaded = len(sw_to_be_downloaded) + + downloaded_ne_sw_info = [] + failed_sw_info = [] + + sw_download_parent_dir = ems_util.get_download_dir(ne_info['omIP']) + logger.info("SW will be downloaded to %s" % sw_download_parent_dir) + + sw_download_dir = ne_info.get("downloadedSwLocation", "") + try: + if not os.path.isdir(sw_download_parent_dir): + os.makedirs(sw_download_parent_dir) + + if sw_download_dir and not os.path.isdir(sw_download_dir): + os.makedirs(sw_download_dir) + except OSError as e: + ret_value["result"] = conf.REQ_FAILURE + ret_value["reason"] = str(e) + + logger.error(ret_value["reason"]) + return ret_value + + if not sw_download_dir: + sw_download_dir = tempfile.mkdtemp(dir=sw_download_parent_dir) + + for sw_info in sw_to_be_downloaded: + err, reason, file_location = do_download(sw_info, sw_download_dir) + if not err: + logger.info("Downloaded SW file location: %s" % file_location) + downloaded_ne_sw_info.append(file_location) + else: + result = conf.REQ_FAILURE + failed_sw_entry = { + "failedSw": file_location, + "failureReason": reason + } + + logger.error("Failed downloaded SW: %s" % str(failed_sw_entry)) + failed_sw_info.append(failed_sw_entry) + + num_downloaded_ne_sw = len(downloaded_ne_sw_info) + + if num_downloaded_ne_sw == num_sw_to_be_downloaded: + download_status = "NE_SWDOWNLOAD_SUCCESSFUL" + elif num_downloaded_ne_sw == 0: + download_status = "NE_SWDOWNLOAD_FAILED" + else: + download_status = "NE_SWDOWNLOAD_PARTIALLY_SUCCESSFUL" + logger.info("Download SW status: %s" % download_status) + + notification = generate_notification(download_process_id, download_status, downloaded_ne_sw_info, failed_sw_info) + ems_util.send_notification(notification, download_process_id) + + if result == conf.REQ_SUCCESS: + ne_info["downloadedSwLocation"] = sw_download_dir + ems_util.update_ne_info(ne_info) + + logger.info("Download SW success") + else: + shutil.rmtree(sw_download_dir, ignore_errors=True) + + ret_value["result"] = result + ret_value["reason"] = json.dumps(failed_sw_info) + + logger.info("Download SW failure, reason: %s" % ret_value["reason"]) + + # for automated software management, there is no listOfStepNumbersAndDurations + return notification, ret_value diff --git a/test/mocks/emssimulator/swm/ems_db/ne_info_table.json b/test/mocks/emssimulator/swm/ems_db/ne_info_table.json index 90aaeb255..5bf014f99 100644 --- a/test/mocks/emssimulator/swm/ems_db/ne_info_table.json +++ b/test/mocks/emssimulator/swm/ems_db/ne_info_table.json @@ -1,39 +1,39 @@ [ { - "status": "Activated", - "nEIdentification": "5gDU0001", - "updateTime": "Tue Apr 23 13:08:43 2019", - "omIP": "192.168.1.1", - "currentSwVersion": "v1", + "status": "Activated", + "nEIdentification": "5gDU0001", + "updateTime": "Tue Apr 23 13:08:43 2019", + "omIP": "192.168.1.1", + "currentSwVersion": "v1", "installedSw": { "ran_du_pkg2-v1": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.1/opt/install/v1/ran_du_pkg2", - "version": "v1", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.1/opt/install/v1/ran_du_pkg2", + "version": "v1", "name": "ran_du_pkg2" - }, + }, "ran_du_pkg1-v1": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.1/opt/install/v1/ran_du_pkg1", - "version": "v1", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.1/opt/install/v1/ran_du_pkg1", + "version": "v1", "name": "ran_du_pkg1" } } - }, + }, { - "status": "Activated", - "nEIdentification": "5gDU0002", - "updateTime": "Tue Apr 23 11:06:36 2019", - "omIP": "192.168.1.2", - "oldSwVersion": "v1", - "currentSwVersion": "v2", + "status": "Activated", + "nEIdentification": "5gDU0002", + "updateTime": "Tue Apr 23 11:06:36 2019", + "omIP": "192.168.1.2", + "oldSwVersion": "v1", + "currentSwVersion": "v2", "installedSw": { "ran_du_pkg1-v2": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.2/opt/install/v2/ran_du_pkg1", - "version": "v2", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.2/opt/install/v2/ran_du_pkg1", + "version": "v2", "name": "ran_du_pkg1" - }, + }, "ran_du_pkg2-v2": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.2/opt/install/v2/ran_du_pkg2", - "version": "v2", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.2/opt/install/v2/ran_du_pkg2", + "version": "v2", "name": "ran_du_pkg2" } } diff --git a/test/mocks/emssimulator/swm/ems_util.py b/test/mocks/emssimulator/swm/ems_util.py index 6d0d3102b..9bfb2cd06 100644 --- a/test/mocks/emssimulator/swm/ems_util.py +++ b/test/mocks/emssimulator/swm/ems_util.py @@ -1,5 +1,22 @@ -#!/usr/bin/python - +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 os import time import json import jsonpath @@ -7,6 +24,18 @@ import jsonpath import conf +def get_log_file(operation): + return os.path.join(conf.LOGGER_FILE_DIR, "%s.txt" % operation) + + +def get_download_dir(om_ip): + return os.path.join(conf.PNF_SIMULATORS_DIR, om_ip, conf.COMMON_PATH, conf.PNF_SW_DOWNLOAD_DIR) + + +def get_install_dir(om_ip): + return os.path.join(conf.PNF_SIMULATORS_DIR, om_ip, conf.COMMON_PATH, conf.PNF_SW_INSTALL_DIR) + + def get_ne_info_list_from_db(ne_filter): with open(conf.NE_INFO_TABLE) as f_ne_info: ne_info_table = json.load(f_ne_info) @@ -47,7 +76,7 @@ def update_ne_info(ne_info): def send_notification(notification, process_id): - notification_file = conf.NOTIFICATION_DIR + '/%s-%d' % (notification['notificationType'], process_id) + notification_file = os.path.join(conf.NOTIFICATION_DIR, '%s-%d' % (notification['notificationType'], process_id)) with open(notification_file, 'w') as f_notification: f_notification.write(json.dumps(notification)) diff --git a/test/mocks/emssimulator/swm/installNESw b/test/mocks/emssimulator/swm/installNESw index 84e2fb9ae..e56f799f7 100755 --- a/test/mocks/emssimulator/swm/installNESw +++ b/test/mocks/emssimulator/swm/installNESw @@ -1,188 +1,28 @@ -#!/usr/bin/python +#!/usr/bin/python3 +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 sys import argparse import json -import os -import shutil -import random -import time -import tempfile -import zipfile import conf -import ems_util - - -def do_install_sw(sw_to_be_installed, ne_info): - """ - return err, reason, installed_ne_sw - """ - - sw_install_dir_in_ne = conf.PNF_SIMULATORS_DIR + '/' + ne_info['omIP'] + conf.PNF_SW_INSTALL_DIR - - if sw_to_be_installed.startswith('/'): - file_location = sw_to_be_installed - else: - sw_download_dir_in_ne = ne_info.get("downloadedSwLocation", "") - file_location = sw_download_dir_in_ne + '/' + sw_to_be_installed - - if not os.access(file_location, os.R_OK): - return True, "Missing to be installed SW file %s" % file_location, None - - try: - if not os.path.isdir(sw_install_dir_in_ne): - os.makedirs(sw_install_dir_in_ne) - except OSError as e: - return True, str(e), None - - temp_dir = tempfile.mkdtemp(dir=sw_install_dir_in_ne) - if file_location.endswith(".zip"): - with zipfile.ZipFile(file_location) as sw_zip: - sw_zip.extractall(temp_dir) - else: - return True, "Only support zip file", None - - manifest_location = temp_dir + '/' + conf.MANIFEST_FILE - if os.access(manifest_location, os.R_OK): - with open(manifest_location) as f_manifest: - manifest = json.load(f_manifest) - else: - shutil.rmtree(temp_dir, ignore_errors=True) - return True, "Missing manifest file in %s" % file_location, None - - try: - target_sw_name = manifest["name"] - target_sw_version = manifest["version"] - except KeyError as e: - shutil.rmtree(temp_dir, ignore_errors=True) - return True, "Missing key %s in %s of %s" % (str(e), conf.MANIFEST_FILE, file_location), None - - if "targetSwVersion" in ne_info and ne_info["targetSwVersion"] != target_sw_version: - shutil.rmtree(temp_dir, ignore_errors=True) - return True, "Conflicted targetVersion for %s" % file_location, None - - ne_info["targetSwVersion"] = target_sw_version - ems_util.update_ne_info(ne_info) - - target_sw_parent_dir = sw_install_dir_in_ne + '/' + target_sw_version - try: - if not os.path.isdir(target_sw_parent_dir): - os.makedirs(target_sw_parent_dir) - except OSError as e: - shutil.rmtree(temp_dir, ignore_errors=True) - return True, str(e), None - - target_sw_dir = target_sw_parent_dir + '/' + target_sw_name - if os.path.isdir(target_sw_dir): - shutil.rmtree(target_sw_dir, ignore_errors=True) - - try: - shutil.move(temp_dir, target_sw_dir) - except shutil.Error as e: - shutil.rmtree(temp_dir, ignore_errors=True) - return True, str(e), None - - installed_ne_sw = target_sw_name + '-' + target_sw_version - - installed_sw_db = target_sw_parent_dir + '/' + conf.INSTALLED_SW - if os.path.isfile(installed_sw_db): - with open(installed_sw_db) as f_installed_sw: - installed_sw_table = json.load(f_installed_sw) - if not installed_sw_table: - installed_sw_table = {} - else: - installed_sw_table = {} - - target_sw_info = { - "name": target_sw_name, - "version": target_sw_version, - "installedLocation": target_sw_dir - } - installed_sw_table[installed_ne_sw] = target_sw_info - - with open(installed_sw_db, 'w') as f_installed_sw: - json.dump(installed_sw_table, f_installed_sw, indent=2) - - ne_info["installedSw"] = installed_sw_table - - return False, None, installed_ne_sw - - -def generate_notification(install_process_id, install_status, installed_ne_sw_info, failed_sw_info): - notification = { - "objectClass": "EMSClass", - "objectInstance": "EMSInstance", - "notificationId": random.randint(1, conf.MAX_INT), - "eventTime": time.asctime(), - "systemDN": "emssimulator", - "notificationType": "notifyInstallNESwStatusChanged", - "installProcessId": install_process_id, - "installOperationStatus": install_status - } - - if installed_ne_sw_info: - notification["installedNESwInfo"] = installed_ne_sw_info - - if failed_sw_info: - notification["failedSwInfo"] = failed_sw_info - - return notification - - -def install_ne_sw(sw_to_be_installed, ne_id): - ne_info = ems_util.get_ne_info_from_db_by_id(ne_id) - - install_process_id = random.randint(1, conf.MAX_INT) - result = conf.REQ_SUCCESS - ret_value = { - "installProcessId": install_process_id, - "result": result - } - - if not ne_info: - ret_value["result"] = conf.REQ_FAILURE - ret_value["reason"] = "Can not find NE %s" % ne_id - return ret_value - - ne_info["status"] = conf.STATUS_INSTALLING - ems_util.update_ne_info(ne_info) - - installed_ne_sw_info = [] - failed_sw_info = [] - - err, reason, installed_ne_sw = do_install_sw(sw_to_be_installed, ne_info) - - if not err: - installed_ne_sw_info.append(installed_ne_sw) - else: - result = conf.REQ_FAILURE - failed_sw_entry = { - "failedSw": installed_ne_sw, - "failureReason": reason - } - failed_sw_info.append(failed_sw_entry) - - num_installed_ne_sw = len(installed_ne_sw_info) - - if num_installed_ne_sw == 1: - install_status = "NE_SWINSTALLATION_SUCCESSFUL" - elif num_installed_ne_sw == 0: - install_status = "NE_SWINSTALLATION_FAILED" - else: - install_status = "NE_SWINSTALLATION_PARTIALLY_SUCCESSFUL" - - notification = generate_notification(install_process_id, install_status, installed_ne_sw_info, failed_sw_info) - ems_util.send_notification(notification, install_process_id) - - if result == conf.REQ_SUCCESS: - ems_util.update_ne_info(ne_info) - else: - ret_value["result"] = result - ret_value["reason"] = json.dumps(failed_sw_info) - - # for automated software management, there is no listOfStepNumbersAndDurations - return ret_value +import install_n_e_sw def main(): @@ -193,8 +33,8 @@ def main(): args = parser.parse_args() - ret_value = install_ne_sw(args.swToBeInstalled, args.neIdentifier) - print json.dumps(ret_value) + _, ret_value = install_n_e_sw.install(args.swToBeInstalled, args.neIdentifier) + print(json.dumps(ret_value)) if ret_value["result"] == conf.REQ_SUCCESS: sys.exit(conf.RET_CODE_SUCCESS) diff --git a/test/mocks/emssimulator/swm/install_n_e_sw.py b/test/mocks/emssimulator/swm/install_n_e_sw.py new file mode 100644 index 000000000..57c59a389 --- /dev/null +++ b/test/mocks/emssimulator/swm/install_n_e_sw.py @@ -0,0 +1,234 @@ +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 os +import logging +import json +import shutil +import random +import time +import tempfile +import zipfile + +import conf +import ems_util + + +OPERATION_NAME = "installNESw" +logging.basicConfig(level=logging.INFO, format=conf.LOGGER_FORMAT, filename=ems_util.get_log_file(OPERATION_NAME)) +logger = logging.getLogger(OPERATION_NAME) + + +def do_install(sw_to_be_installed, ne_info): + """ + return err, reason, installed_ne_sw + """ + + logger.info("swToBeInstalled: %s" % sw_to_be_installed) + + sw_install_dir_in_ne = ems_util.get_install_dir(ne_info['omIP']) + + if sw_to_be_installed.startswith('/'): + file_location = sw_to_be_installed + else: + sw_download_dir_in_ne = ne_info.get("downloadedSwLocation", "") + file_location = os.path.join(sw_download_dir_in_ne, sw_to_be_installed) + + if not os.access(file_location, os.R_OK): + msg = "Missing to be installed SW file %s" % file_location + logger.error(msg) + return True, msg, None + + try: + if not os.path.isdir(sw_install_dir_in_ne): + os.makedirs(sw_install_dir_in_ne) + except OSError as e: + msg = str(e) + logger.error(msg) + return True, msg, None + + temp_dir = tempfile.mkdtemp(dir=sw_install_dir_in_ne) + if file_location.endswith(".zip"): + with zipfile.ZipFile(file_location) as sw_zip: + sw_zip.extractall(temp_dir) + else: + msg = "Only support zip file" + logger.error(msg) + return True, msg, None + + manifest_location = os.path.join(temp_dir, conf.MANIFEST_FILE) + if os.access(manifest_location, os.R_OK): + with open(manifest_location) as f_manifest: + manifest = json.load(f_manifest) + else: + shutil.rmtree(temp_dir, ignore_errors=True) + msg = "Missing manifest file in %s" % file_location + logger.error(msg) + return True, msg, None + + try: + target_sw_name = manifest["name"] + target_sw_version = manifest["version"] + except KeyError as e: + shutil.rmtree(temp_dir, ignore_errors=True) + msg = "Missing key %s in %s of %s" % (str(e), conf.MANIFEST_FILE, file_location) + logger.error(msg) + return True, msg, None + + if "targetSwVersion" in ne_info and ne_info["targetSwVersion"] != target_sw_version: + shutil.rmtree(temp_dir, ignore_errors=True) + msg = "Conflicted targetVersion for %s" % file_location + logger.error(msg) + return True, msg, None + + ne_info["targetSwVersion"] = target_sw_version + ems_util.update_ne_info(ne_info) + + target_sw_parent_dir = os.path.join(sw_install_dir_in_ne, target_sw_version) + try: + if not os.path.isdir(target_sw_parent_dir): + os.makedirs(target_sw_parent_dir) + except OSError as e: + shutil.rmtree(temp_dir, ignore_errors=True) + msg = str(e) + logger.error(msg) + return True, msg, None + + target_sw_dir = os.path.join(target_sw_parent_dir, target_sw_name) + if os.path.isdir(target_sw_dir): + shutil.rmtree(target_sw_dir, ignore_errors=True) + + try: + shutil.move(temp_dir, target_sw_dir) + except shutil.Error as e: + shutil.rmtree(temp_dir, ignore_errors=True) + msg = str(e) + logger.error(msg) + return True, msg, None + logger.info("Install SW to %s" % target_sw_dir) + + installed_ne_sw = target_sw_name + '-' + target_sw_version + logger.info("Installed SW: %s" % installed_ne_sw) + + installed_sw_db = os.path.join(target_sw_parent_dir, conf.INSTALLED_SW_FILE) + if os.path.isfile(installed_sw_db): + with open(installed_sw_db) as f_installed_sw: + installed_sw_table = json.load(f_installed_sw) + if not installed_sw_table: + installed_sw_table = {} + else: + installed_sw_table = {} + + target_sw_info = { + "name": target_sw_name, + "version": target_sw_version, + "installedLocation": target_sw_dir + } + installed_sw_table[installed_ne_sw] = target_sw_info + + with open(installed_sw_db, 'w') as f_installed_sw: + json.dump(installed_sw_table, f_installed_sw, indent=2) + + ne_info["installedSw"] = installed_sw_table + + return False, None, installed_ne_sw + + +def generate_notification(install_process_id, install_status, installed_ne_sw_info, failed_sw_info): + notification = { + "objectClass": conf.OBJECT_CLASS, + "objectInstance": conf.OBJECT_INSTANCE, + "notificationId": random.randint(1, conf.MAX_INT), + "eventTime": time.asctime(), + "systemDN": conf.SYSTEM_DN, + "notificationType": "notifyInstallNESwStatusChanged", + "installProcessId": install_process_id, + "installOperationStatus": install_status + } + + if installed_ne_sw_info: + notification["installedNESwInfo"] = installed_ne_sw_info + + if failed_sw_info: + notification["failedSwInfo"] = failed_sw_info + + return notification + + +def install(sw_to_be_installed, ne_id): + ne_info = ems_util.get_ne_info_from_db_by_id(ne_id) + + install_process_id = random.randint(1, conf.MAX_INT) + result = conf.REQ_SUCCESS + ret_value = { + "installProcessId": install_process_id, + "result": result + } + + if not ne_info: + ret_value["result"] = conf.REQ_FAILURE + ret_value["reason"] = "Can not find NE %s" % ne_id + + logger.error(ret_value["reason"]) + return ret_value + + ne_info["status"] = conf.STATUS_INSTALLING + ems_util.update_ne_info(ne_info) + + installed_ne_sw_info = [] + failed_sw_info = [] + + err, reason, installed_ne_sw = do_install(sw_to_be_installed, ne_info) + + if not err: + installed_ne_sw_info.append(installed_ne_sw) + else: + result = conf.REQ_FAILURE + failed_sw_entry = { + "failedSw": installed_ne_sw, + "failureReason": reason + } + + logger.error("Failed installed SW: %s" % str(failed_sw_entry)) + failed_sw_info.append(failed_sw_entry) + + num_installed_ne_sw = len(installed_ne_sw_info) + + if num_installed_ne_sw == 1: + install_status = "NE_SWINSTALLATION_SUCCESSFUL" + elif num_installed_ne_sw == 0: + install_status = "NE_SWINSTALLATION_FAILED" + else: + install_status = "NE_SWINSTALLATION_PARTIALLY_SUCCESSFUL" + logger.info("Install SW status: %s" % install_status) + + notification = generate_notification(install_process_id, install_status, installed_ne_sw_info, failed_sw_info) + ems_util.send_notification(notification, install_process_id) + + if result == conf.REQ_SUCCESS: + ems_util.update_ne_info(ne_info) + + logger.info("Install SW success") + else: + ret_value["result"] = result + ret_value["reason"] = json.dumps(failed_sw_info) + + logger.info("Install SW failure, reason: %s" % ret_value["reason"]) + + # for automated software management, there is no listOfStepNumbersAndDurations + return notification, ret_value diff --git a/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.1/opt/install/v1/installed_sw.json b/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.1/opt/install/v1/installed_sw.json index 9c1921521..e7f13a5dd 100644 --- a/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.1/opt/install/v1/installed_sw.json +++ b/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.1/opt/install/v1/installed_sw.json @@ -1,12 +1,12 @@ { "ran_du_pkg2-v1": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.1/opt/install/v1/ran_du_pkg2", - "version": "v1", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.1/opt/install/v1/ran_du_pkg2", + "version": "v1", "name": "ran_du_pkg2" - }, + }, "ran_du_pkg1-v1": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.1/opt/install/v1/ran_du_pkg1", - "version": "v1", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.1/opt/install/v1/ran_du_pkg1", + "version": "v1", "name": "ran_du_pkg1" } } diff --git a/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.2/opt/install/v1/installed_sw.json b/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.2/opt/install/v1/installed_sw.json index 31cddf9a1..bd63618c2 100644 --- a/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.2/opt/install/v1/installed_sw.json +++ b/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.2/opt/install/v1/installed_sw.json @@ -1,12 +1,12 @@ { "ran_du_pkg2-v1": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.2/opt/install/v1/ran_du_pkg2", - "version": "v1", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.2/opt/install/v1/ran_du_pkg2", + "version": "v1", "name": "ran_du_pkg2" - }, + }, "ran_du_pkg1-v1": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.2/opt/install/v1/ran_du_pkg1", - "version": "v1", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.2/opt/install/v1/ran_du_pkg1", + "version": "v1", "name": "ran_du_pkg1" } } diff --git a/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.2/opt/install/v2/installed_sw.json b/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.2/opt/install/v2/installed_sw.json index e55c7f5f0..3fed1839f 100644 --- a/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.2/opt/install/v2/installed_sw.json +++ b/test/mocks/emssimulator/swm/pnf_simulators/192.168.1.2/opt/install/v2/installed_sw.json @@ -1,12 +1,12 @@ { "ran_du_pkg1-v2": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.2/opt/install/v2/ran_du_pkg1", - "version": "v2", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.2/opt/install/v2/ran_du_pkg1", + "version": "v2", "name": "ran_du_pkg1" - }, + }, "ran_du_pkg2-v2": { - "installedLocation": "/home/ubuntu/swm/pnf_simulators/192.168.1.2/opt/install/v2/ran_du_pkg2", - "version": "v2", + "installedLocation": "/home/admin/swm/pnf_simulators/192.168.1.2/opt/install/v2/ran_du_pkg2", + "version": "v2", "name": "ran_du_pkg2" } } diff --git a/test/mocks/emssimulator/swm/swFallback b/test/mocks/emssimulator/swm/swFallback index 9d6608c23..506eee0d4 100755 --- a/test/mocks/emssimulator/swm/swFallback +++ b/test/mocks/emssimulator/swm/swFallback @@ -1,108 +1,29 @@ -#!/usr/bin/python +#!/usr/bin/python3 +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 sys import argparse import json -import sys -import os -import shutil import conf import ems_util - - -def sw_fallback(ne_info_list): - ne_list = [] - num_failure = 0 - - for ne_info in ne_info_list: - if ne_info.get("status") == conf.STATUS_DOWNLOADING: - ne_info["status"] = conf.STATUS_ACTIVATED - ems_util.update_ne_info(ne_info) - - ne_entry = { - "nEIdentification": ne_info["nEIdentification"], - "swFallbackStatus": "fallbackSuccessful" - } - ne_list.append(ne_entry) - continue - - sw_install_dir_in_ne = conf.PNF_SIMULATORS_DIR + '/' + ne_info['omIP'] + conf.PNF_SW_INSTALL_DIR - - if ne_info.get("status") == conf.STATUS_INSTALLING: - old_sw_version = ne_info.get("currentSwVersion", "") - current_sw_version = ne_info.get("targetSwVersion", "") - else: - old_sw_version = ne_info.get("oldSwVersion", "") - current_sw_version = ne_info.get("currentSwVersion", "") - - old_sw_dir = sw_install_dir_in_ne + '/' + old_sw_version - - if not old_sw_version or not os.path.isdir(old_sw_dir): - ne_entry = { - "nEIdentification": ne_info["nEIdentification"], - "swFallbackStatus": "fallbackUnsuccessful" - } - ne_list.append(ne_entry) - - num_failure += 1 - continue - - current_sw_dir = sw_install_dir_in_ne + '/' + current_sw_version - - if current_sw_version and os.path.isdir(current_sw_dir) and current_sw_dir != old_sw_dir: - shutil.rmtree(current_sw_dir, ignore_errors=True) - - old_cwd = os.getcwd() - os.chdir(sw_install_dir_in_ne) - if os.path.islink(conf.CURRENT_VERSION_DIR): - os.remove(conf.CURRENT_VERSION_DIR) - os.symlink(old_sw_version, conf.CURRENT_VERSION_DIR) - os.chdir(old_cwd) - - installed_sw_db = old_sw_dir + '/' + conf.INSTALLED_SW - if os.path.isfile(installed_sw_db): - with open(installed_sw_db) as f_installed_sw: - installed_sw_table = json.load(f_installed_sw) - if not installed_sw_table: - installed_sw_table = {} - else: - installed_sw_table = {} - - ne_info["installedSw"] = installed_sw_table - if "oldSwVersion" in ne_info: - ne_info["currentSwVersion"] = ne_info["oldSwVersion"] - del ne_info["oldSwVersion"] - - if "targetSwVersion" in ne_info: - del ne_info["targetSwVersion"] - - if "downloadedSwLocation" in ne_info: - if os.path.isdir(ne_info["downloadedSwLocation"]): - shutil.rmtree(ne_info["downloadedSwLocation"], ignore_errors=True) - del ne_info["downloadedSwLocation"] - - ne_info["status"] = conf.STATUS_ACTIVATED - ems_util.update_ne_info(ne_info) - - ne_entry = { - "nEIdentification": ne_info["nEIdentification"], - "swFallbackStatus": "fallbackSuccessful" - } - ne_list.append(ne_entry) - - if num_failure == 0: - result = conf.RESULT_SUCCESS - elif num_failure == len(ne_info_list): - result = conf.RESULT_FAILURE - else: - result = conf.RESULT_PARTLY - - ret_value = { - "nEList": ne_list, - "result": result - } - - return ret_value +import sw_fallback def main(): @@ -114,8 +35,8 @@ def main(): ne_info_list = ems_util.get_ne_info_list_from_db(args.filter) - ret_value = sw_fallback(ne_info_list) - print json.dumps(ret_value) + ret_value = sw_fallback.fallback(ne_info_list) + print(json.dumps(ret_value)) if ret_value["result"] == conf.RESULT_SUCCESS: sys.exit(conf.RET_CODE_SUCCESS) diff --git a/test/mocks/emssimulator/swm/sw_fallback.py b/test/mocks/emssimulator/swm/sw_fallback.py new file mode 100644 index 000000000..3037d4575 --- /dev/null +++ b/test/mocks/emssimulator/swm/sw_fallback.py @@ -0,0 +1,129 @@ +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 os +import logging +import json +import shutil + +import conf +import ems_util + + +OPERATION_NAME = "swFallback" +logging.basicConfig(level=logging.INFO, format=conf.LOGGER_FORMAT, filename=ems_util.get_log_file(OPERATION_NAME)) +logger = logging.getLogger(OPERATION_NAME) + + +def fallback(ne_info_list): + logger.info("NE info list: %s" % ne_info_list) + + ne_list = [] + num_failure = 0 + + for ne_info in ne_info_list: + if ne_info.get("status") == conf.STATUS_DOWNLOADING: + ne_info["status"] = conf.STATUS_ACTIVATED + ems_util.update_ne_info(ne_info) + + ne_entry = { + "nEIdentification": ne_info["nEIdentification"], + "swFallbackStatus": "fallbackSuccessful" + } + ne_list.append(ne_entry) + continue + + sw_install_dir_in_ne = ems_util.get_install_dir(ne_info['omIP']) + + if ne_info.get("status") == conf.STATUS_INSTALLING: + old_sw_version = ne_info.get("currentSwVersion", "") + current_sw_version = ne_info.get("targetSwVersion", "") + else: + old_sw_version = ne_info.get("oldSwVersion", "") + current_sw_version = ne_info.get("currentSwVersion", "") + + old_sw_dir = os.path.join(sw_install_dir_in_ne, old_sw_version) + + if not old_sw_version or not os.path.isdir(old_sw_dir): + ne_entry = { + "nEIdentification": ne_info["nEIdentification"], + "swFallbackStatus": "fallbackUnsuccessful" + } + logger.error("oldSwVersion (%s) or oldSwDirectory (%s) is none" % (old_sw_version, old_sw_dir)) + ne_list.append(ne_entry) + + num_failure += 1 + continue + + current_sw_dir = os.path.join(sw_install_dir_in_ne, current_sw_version) + + if current_sw_version and os.path.isdir(current_sw_dir) and current_sw_dir != old_sw_dir: + shutil.rmtree(current_sw_dir, ignore_errors=True) + + old_cwd = os.getcwd() + os.chdir(sw_install_dir_in_ne) + if os.path.islink(conf.CURRENT_VERSION_DIR): + os.remove(conf.CURRENT_VERSION_DIR) + os.symlink(old_sw_version, conf.CURRENT_VERSION_DIR) + os.chdir(old_cwd) + + installed_sw_db = os.path.join(old_sw_dir, conf.INSTALLED_SW_FILE) + if os.path.isfile(installed_sw_db): + with open(installed_sw_db) as f_installed_sw: + installed_sw_table = json.load(f_installed_sw) + if not installed_sw_table: + installed_sw_table = {} + else: + installed_sw_table = {} + + ne_info["installedSw"] = installed_sw_table + if "oldSwVersion" in ne_info: + ne_info["currentSwVersion"] = ne_info["oldSwVersion"] + del ne_info["oldSwVersion"] + + if "targetSwVersion" in ne_info: + del ne_info["targetSwVersion"] + + if "downloadedSwLocation" in ne_info: + if os.path.isdir(ne_info["downloadedSwLocation"]): + shutil.rmtree(ne_info["downloadedSwLocation"], ignore_errors=True) + del ne_info["downloadedSwLocation"] + + ne_info["status"] = conf.STATUS_ACTIVATED + ems_util.update_ne_info(ne_info) + + ne_entry = { + "nEIdentification": ne_info["nEIdentification"], + "swFallbackStatus": "fallbackSuccessful" + } + ne_list.append(ne_entry) + + if num_failure == 0: + result = conf.RESULT_SUCCESS + elif num_failure == len(ne_info_list): + result = conf.RESULT_FAILURE + else: + result = conf.RESULT_PARTLY + logger.info("Fallback SW result: %s" % result) + + ret_value = { + "nEList": ne_list, + "result": result + } + + return ret_value diff --git a/test/mocks/emssimulator/swm/upgrade-post-check b/test/mocks/emssimulator/swm/upgrade-post-check index 799afbccc..e5dfae573 100755 --- a/test/mocks/emssimulator/swm/upgrade-post-check +++ b/test/mocks/emssimulator/swm/upgrade-post-check @@ -1,74 +1,52 @@ -#!/usr/bin/python +#!/usr/bin/python3 +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 sys import json import conf -import ems_util - - -def upgrade_postcheck(pnf_id, old_sw_version, target_sw_version, rule_name, tmp_file=None): - ne_info = ems_util.get_ne_info_from_db_by_id(pnf_id) - - if not ne_info: - ret_value = { - "result": conf.RESULT_FAILURE, - "reason": "Can not find NE %s" % pnf_id - } - return ret_value - - old_sw_version_in_db = ne_info.get("oldSwVersion", "") - current_sw_version_in_db = ne_info.get("currentSwVersion", "") - - if old_sw_version != old_sw_version_in_db: - ret_value = { - "result": conf.RESULT_FAILURE, - "reason": "Old SW version %s in PNF is not matched with oldSwVersion %s" % - (old_sw_version_in_db, old_sw_version) - } - return ret_value - - if target_sw_version != current_sw_version_in_db: - ret_value = { - "result": conf.RESULT_FAILURE, - "reason": "Current SW version %s in PNF is not matched with targetSwVersion %s" % - (current_sw_version_in_db, target_sw_version) - } - return ret_value - - ne_info["checkStatus"] = conf.STATUS_POSTCHECKED - ems_util.update_ne_info(ne_info) - - ret_value = { - "result": conf.RESULT_SUCCESS - } - - return ret_value +import upgrade_post_check def main(): - # {{pnfId}} {{oldSwVersion}} {{targetSwVersion}} {{ruleName}} /tmp/tmp-{{Id}} + # {{pnfName}} {{oldSwVersion}} {{targetSwVersion}} {{ruleName}} {{additionalDataFile}} if len(sys.argv) < 5: ret_value = { "result": conf.RESULT_FAILURE, "reason": "Missing parameters" } - print json.dumps(ret_value) + print(json.dumps(ret_value)) sys.exit(conf.RET_CODE_FAILURE) - - if len(sys.argv) >= 5: - pnf_id = sys.argv[1] + else: + pnf_name = sys.argv[1] old_sw_version = sys.argv[2] target_sw_version = sys.argv[3] rule_name = sys.argv[4] - tmp_file = None + additional_data_file = None if len(sys.argv) >= 6: - tmp_file = sys.argv[5] + additional_data_file = sys.argv[5] - ret_value = upgrade_postcheck(pnf_id, old_sw_version, target_sw_version, rule_name, tmp_file) - print json.dumps(ret_value) + ret_value = upgrade_post_check.post_check( + pnf_name, old_sw_version, target_sw_version, rule_name, additional_data_file) + print(json.dumps(ret_value)) if ret_value["result"] == conf.RESULT_SUCCESS: sys.exit(conf.RET_CODE_SUCCESS) diff --git a/test/mocks/emssimulator/swm/upgrade-pre-check b/test/mocks/emssimulator/swm/upgrade-pre-check index 6bf867f48..5b0726b63 100755 --- a/test/mocks/emssimulator/swm/upgrade-pre-check +++ b/test/mocks/emssimulator/swm/upgrade-pre-check @@ -1,65 +1,52 @@ -#!/usr/bin/python +#!/usr/bin/python3 +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 sys import json import conf -import ems_util - - -def upgrade_precheck(pnf_id, old_sw_version, target_sw_version, rule_name, tmp_file=None): - ne_info = ems_util.get_ne_info_from_db_by_id(pnf_id) - - if not ne_info: - ret_value = { - "result": conf.RESULT_FAILURE, - "reason": "Can not find NE %s" % pnf_id - } - return ret_value - - current_sw_version_in_db = ne_info.get("currentSwVersion", "") - - if old_sw_version != current_sw_version_in_db: - ret_value = { - "result": conf.RESULT_FAILURE, - "reason": "Current SW version %s in PNF is not matched with oldSwVersion %s" % - (current_sw_version_in_db, old_sw_version) - } - return ret_value - - ne_info["checkStatus"] = conf.STATUS_PRECHECKED - ems_util.update_ne_info(ne_info) - - ret_value = { - "result": conf.RESULT_SUCCESS - } - - return ret_value +import upgrade_pre_check def main(): - # {{pnfId}} {{oldSwVersion}} {{targetSwVersion}} {{ruleName}} /tmp/tmp-{{Id}} + # {{pnfName}} {{oldSwVersion}} {{targetSwVersion}} {{ruleName}} {{additionalDataFile}} if len(sys.argv) < 5: ret_value = { "result": conf.RESULT_FAILURE, "reason": "Missing parameters" } - print json.dumps(ret_value) + print(json.dumps(ret_value)) sys.exit(conf.RET_CODE_FAILURE) - - if len(sys.argv) >= 5: - pnf_id = sys.argv[1] + else: + pnf_name = sys.argv[1] old_sw_version = sys.argv[2] target_sw_version = sys.argv[3] rule_name = sys.argv[4] - tmp_file = None + additional_data_file = None if len(sys.argv) >= 6: - tmp_file = sys.argv[5] + additional_data_file = sys.argv[5] - ret_value = upgrade_precheck(pnf_id, old_sw_version, target_sw_version, rule_name, tmp_file) - print json.dumps(ret_value) + ret_value = upgrade_pre_check.pre_check( + pnf_name, old_sw_version, target_sw_version, rule_name, additional_data_file) + print(json.dumps(ret_value)) if ret_value["result"] == conf.RESULT_SUCCESS: sys.exit(conf.RET_CODE_SUCCESS) diff --git a/test/mocks/emssimulator/swm/upgrade_post_check.py b/test/mocks/emssimulator/swm/upgrade_post_check.py new file mode 100644 index 000000000..5140e0603 --- /dev/null +++ b/test/mocks/emssimulator/swm/upgrade_post_check.py @@ -0,0 +1,76 @@ +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 conf +import ems_util + + +OPERATION_NAME = "upgrade-post-check" +logging.basicConfig(level=logging.INFO, format=conf.LOGGER_FORMAT, filename=ems_util.get_log_file(OPERATION_NAME)) +logger = logging.getLogger(OPERATION_NAME) + + +def post_check(pnf_name, old_sw_version, target_sw_version, rule_name, additional_data_file=None): + logger.info("PostCheck for oldSwVersion: %s, targetSwVersion: %s, ruleName: %s, additionalDataFile: %s" % + (old_sw_version, target_sw_version, rule_name, additional_data_file)) + + ne_info = ems_util.get_ne_info_from_db_by_id(pnf_name) + + if not ne_info: + ret_value = { + "result": conf.RESULT_FAILURE, + "reason": "Can not find NE %s" % pnf_name + } + + logger.error(ret_value["reason"]) + return ret_value + + old_sw_version_in_db = ne_info.get("oldSwVersion", "") + current_sw_version_in_db = ne_info.get("currentSwVersion", "") + + if old_sw_version != old_sw_version_in_db: + ret_value = { + "result": conf.RESULT_FAILURE, + "reason": "Old SW version %s in PNF is not matched with oldSwVersion %s" % + (old_sw_version_in_db, old_sw_version) + } + + logger.error(ret_value["reason"]) + return ret_value + + if target_sw_version != current_sw_version_in_db: + ret_value = { + "result": conf.RESULT_FAILURE, + "reason": "Current SW version %s in PNF is not matched with targetSwVersion %s" % + (current_sw_version_in_db, target_sw_version) + } + + logger.error(ret_value["reason"]) + return ret_value + + ne_info["checkStatus"] = conf.STATUS_POSTCHECKED + ems_util.update_ne_info(ne_info) + logger.info("PostCheck SW success, check status: %s" % ne_info["checkStatus"]) + + ret_value = { + "result": conf.RESULT_SUCCESS + } + + return ret_value diff --git a/test/mocks/emssimulator/swm/upgrade_pre_check.py b/test/mocks/emssimulator/swm/upgrade_pre_check.py new file mode 100644 index 000000000..ee0423acc --- /dev/null +++ b/test/mocks/emssimulator/swm/upgrade_pre_check.py @@ -0,0 +1,65 @@ +# ============LICENSE_START======================================================= +# ONAP - SO +# ================================================================================ +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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 conf +import ems_util + + +OPERATION_NAME = "upgrade-pre-check" +logging.basicConfig(level=logging.INFO, format=conf.LOGGER_FORMAT, filename=ems_util.get_log_file(OPERATION_NAME)) +logger = logging.getLogger(OPERATION_NAME) + + +def pre_check(pnf_name, old_sw_version, target_sw_version, rule_name, additional_data_file=None): + logger.info("PreCheck for oldSwVersion: %s, targetSwVersion: %s, ruleName: %s, additionalDataFile: %s" % + (old_sw_version, target_sw_version, rule_name, additional_data_file)) + + ne_info = ems_util.get_ne_info_from_db_by_id(pnf_name) + + if not ne_info: + ret_value = { + "result": conf.RESULT_FAILURE, + "reason": "Can not find NE %s" % pnf_name + } + + logger.error(ret_value["reason"]) + return ret_value + + current_sw_version_in_db = ne_info.get("currentSwVersion", "") + + if old_sw_version != current_sw_version_in_db: + ret_value = { + "result": conf.RESULT_FAILURE, + "reason": "Current SW version %s in PNF is not matched with oldSwVersion %s" % + (current_sw_version_in_db, old_sw_version) + } + + logger.error(ret_value["reason"]) + return ret_value + + ne_info["checkStatus"] = conf.STATUS_PRECHECKED + ems_util.update_ne_info(ne_info) + logger.info("PreCheck SW success, check status: %s" % ne_info["checkStatus"]) + + ret_value = { + "result": conf.RESULT_SUCCESS + } + + return ret_value |