2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2020 Nordix Foundation.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 * ============LICENSE_END=========================================================
22 import com.fasterxml.jackson.databind.node.ObjectNode
23 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
24 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.contentFromResolvedArtifactNB
25 import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.restconfApplyDeviceConfig
26 import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.restconfDeviceConfig
27 import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.restconfMountDevice
28 import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.restconfUnMountDevice
29 import org.onap.ccsdk.cds.blueprintsprocessor.rest.restClientService
30 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
31 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
32 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
33 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintRetryException
34 import org.onap.ccsdk.cds.controllerblueprints.core.logger
35 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
36 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
38 class RestconfSoftwareUpgrade : AbstractScriptComponentFunction() {
40 private val RESTCONF_SERVER_IDENTIFIER = "sdnc"
41 private val CONFIGLET_RESOURCE_PATH = "yang-ext:mount/pnf-sw-upgrade:software-upgrade"
42 private val log = logger(AbstractScriptComponentFunction::class.java)
43 private val TARGET_SOFTWARE_PATH = "$CONFIGLET_RESOURCE_PATH/upgrade-package/"
45 override suspend fun processNB(executionRequest: ExecutionServiceInput) {
47 // Extract request properties
48 val properties = requestPayloadActionProperty(executionRequest.actionIdentifiers.actionName + "-properties")!!.get(0)
49 val model= SoftwareUpgradeModel(getDynamicProperties("resolution-key").asText(),
50 BluePrintDependencyService.restClientService(RESTCONF_SERVER_IDENTIFIER),
51 properties.get("pnf-id").textValue(), properties.get("target-software-version").textValue(),
52 Action.getEnumFromActionName(executionRequest.actionIdentifiers.actionName))
54 log.info("Blueprint invoked for ${model.resolutionKey} for SW Upgrade : " +
55 "${model.action} for sw version ${model.targetSwVersion} on pnf: ${model.deviceId}")
58 val mountPayload = contentFromResolvedArtifactNB("mount-node")
59 log.debug("Mount Payload : $mountPayload")
60 restconfMountDevice(model.client, model.deviceId, mountPayload, mutableMapOf("Content-Type" to "application/json"))
63 Action.PRE_CHECK -> processPrecheck(model)
64 Action.DOWNLOAD_NE_SW -> processDownloadNeSw(model)
65 Action.ACTIVATE_NE_SW -> processActivateNeSw(model)
66 Action.POST_CHECK -> processPostcheck(model)
67 Action.CANCEL -> processCancel(model)
70 } catch (err: Exception) {
71 log.error("an error occurred while configuring device {}", err)
73 restconfUnMountDevice(model.client, model.deviceId, "")
77 private suspend fun processPrecheck(model: SoftwareUpgradeModel) {
78 log.debug("In PNF SW upgrade : processPreCheck")
79 //Log the current configuration for the subtree
80 val payloadObject = getCurrentConfig(model)
81 log.debug("Current sw version on pnf : ${payloadObject.get("software-upgrade")?.get("upgrade-package")?.get(0)?.get("software-version")?.asText()}")
82 log.info("PNF is Healthy!")
85 private suspend fun processDownloadNeSw(model: SoftwareUpgradeModel) {
86 log.debug("In PNF SW upgrade : processDownloadNeSw")
87 //Check if there is existing config for the targeted software version
89 var downloadConfigPayload: String
90 if (checkIfSwReadyToPerformAction(Action.PRE_CHECK, model)) {
91 downloadConfigPayload = contentFromResolvedArtifactNB("configure")
92 downloadConfigPayload =downloadConfigPayload.replace("%id%", model.yangId)
95 downloadConfigPayload = contentFromResolvedArtifactNB("download-ne-sw")
96 model.yangId=model.targetSwVersion
98 downloadConfigPayload = downloadConfigPayload.replace("%actionName%", Action.DOWNLOAD_NE_SW.name)
99 log.info("Config Payload to start download : $downloadConfigPayload")
102 restconfApplyDeviceConfig(model.client, model.deviceId, CONFIGLET_RESOURCE_PATH, downloadConfigPayload,
103 mutableMapOf("Content-Type" to "application/yang.patch+json"))
105 //Poll PNF for Download action's progress
106 checkExecution(model)
109 private suspend fun processActivateNeSw(model: SoftwareUpgradeModel) {
110 log.debug("In PNF SW upgrade : processActivateNeSw")
111 //Check if the software is downloaded and ready to be activated
112 if (checkIfSwReadyToPerformAction(Action.DOWNLOAD_NE_SW, model)) {
113 var activateConfigPayload: String = contentFromResolvedArtifactNB("configure")
114 activateConfigPayload = activateConfigPayload.replace("%actionName%", Action.ACTIVATE_NE_SW.name)
115 log.info("Config Payload to start activate : $activateConfigPayload")
117 restconfApplyDeviceConfig(model.client, model.deviceId, CONFIGLET_RESOURCE_PATH, activateConfigPayload,
118 mutableMapOf("Content-Type" to "application/yang.patch+json"))
120 //Poll PNF for Activate action's progress
121 checkExecution(model)
123 throw BluePrintRetryException("Software Download not completed for device(${model.deviceId}) to activate sw version: ${model.targetSwVersion}")
127 private suspend fun processPostcheck(model: SoftwareUpgradeModel) {
128 log.info("In PNF SW upgrade : processPostcheck")
129 //Log the current configuration for the subtree
130 if (checkIfSwReadyToPerformAction(Action.POST_CHECK, model)) {
131 log.info("PNF is healthy post activation!")
135 private fun processCancel(model :SoftwareUpgradeModel) {
136 //This is for future implementation of cancel step during software upgrade
137 log.info("In PNF SW upgrade : processCancel")
140 private suspend fun getCurrentConfig(model: SoftwareUpgradeModel) : ObjectNode{
141 val currentConfig: BlueprintWebClientService.WebClientResponse<String> = restconfDeviceConfig(model.client, model.deviceId, CONFIGLET_RESOURCE_PATH)
142 return JacksonUtils.jsonNode(currentConfig.body) as ObjectNode
144 private suspend fun checkExecution(model: SoftwareUpgradeModel) {
145 val checkExecutionBlock: suspend (Int) -> String = {
146 val result = restconfDeviceConfig(model.client, model.deviceId, TARGET_SOFTWARE_PATH.plus(model.yangId))
147 if (result.body.contains(model.action.completionStatus)) {
148 log.info("${model.action.name} is complete")
151 throw BluePrintRetryException("Waiting for device(${model.deviceId}) to activate sw version ${model.targetSwVersion}")
154 model.client.retry<String>(10, 0, 1000, checkExecutionBlock)
158 private suspend fun checkIfSwReadyToPerformAction(action : Action, model: SoftwareUpgradeModel): Boolean {
159 val configBody = getCurrentConfig(model)
160 configBody.get("software-upgrade")?.get("upgrade-package")?.iterator()?.forEach { item ->
161 if (model.targetSwVersion == item.get("software-version")?.asText() &&
162 action.completionStatus == item?.get("current-status")?.asText()) {
163 model.yangId= item.get("id").textValue()
170 override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
171 log.info("Recover function called!")
172 log.info("Execution request : $executionRequest")
173 log.error("Exception", runtimeException)
177 enum class Action(val actionName: String, val completionStatus: String) {
178 PRE_CHECK("precheck", "INITIALIZED"),
179 DOWNLOAD_NE_SW("downloadNeSw", "DOWNLOAD_COMPLETED"),
180 ACTIVATE_NE_SW("activateNeSw", "ACTIVATION_COMPLETED"),
181 POST_CHECK("postcheck", "ACTIVATION_COMPLETED"),
182 CANCEL("cancel", "CANCELLED")
185 fun getEnumFromActionName(name: String): Action {
186 for(value in values()){
187 if (value.actionName==name) return value
189 throw BluePrintException("Invalid Action sent to CDS")
194 data class SoftwareUpgradeModel(val resolutionKey: String, val client: BlueprintWebClientService, val deviceId: String,
195 val targetSwVersion: String, val action: Action, var yangId: String = "")