diff options
author | Claudio D. Gasparini <claudio.gasparini@intl.att.com> | 2021-03-29 10:02:17 +0200 |
---|---|---|
committer | Claudio David Gasparini <claudio.gasparini@intl.att.com> | 2021-03-30 14:56:08 +0000 |
commit | 3ca2faef79e4a69cd03f202ab1ae26b4e564e743 (patch) | |
tree | 87791fd1befe7c697c29d8e0d33b4aaa2cc90e1b | |
parent | 53341bfd9e0ed5511ae5246cecc6a840e0dd1136 (diff) |
Extend Restconf executor function
provide capability to
- execute a sorted array of restconf actions
- mount odl restconf node
Issue-ID: CCSDK-3241
Signed-off-by: Claudio D. Gasparini <claudio.gasparini@intl.att.com>
Change-Id: I1999195f7b84a259d82f9c5aa31e9fa892e9c3df
7 files changed, 366 insertions, 74 deletions
diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfConfigDeploy.kt b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfConfigDeploy.kt index 78ab34e9e..d422b7d67 100644 --- a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfConfigDeploy.kt +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfConfigDeploy.kt @@ -77,7 +77,7 @@ class RestconfConfigDeploy : AbstractScriptComponentFunction() { log.error("an error occurred while configuring device {}", err) } finally { // Un mount device - restconfUnMountDevice(webclientService, deviceID, "") + restconfUnMountDevice(webclientService, deviceID) } } catch (bpe: BlueprintProcessorException) { log.error("Error looking up server identifier ", bpe) diff --git a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfSoftwareUpgrade.kt b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfSoftwareUpgrade.kt index 96345f0d3..f2335c138 100644 --- a/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfSoftwareUpgrade.kt +++ b/components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfSoftwareUpgrade.kt @@ -66,7 +66,7 @@ class RestconfSoftwareUpgrade : AbstractScriptComponentFunction() { } catch (err: Exception) { log.error("an error occurred while configuring device {}", err) } finally { - restconfUnMountDevice(model.client, model.deviceId, "") + restconfUnMountDevice(model.client, model.deviceId) } } diff --git a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Scripts/kotlin/RestconfConfigDeploy.kt b/components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Scripts/kotlin/RestconfConfigDeploy.kt index 2ba527ac1..039685575 100644 --- a/components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Scripts/kotlin/RestconfConfigDeploy.kt +++ b/components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Scripts/kotlin/RestconfConfigDeploy.kt @@ -66,7 +66,7 @@ class RestconfConfigDeploy : AbstractScriptComponentFunction() { log.error("an error occurred while configuring device {}", err) } finally { // Un mount device - restconfUnMountDevice(webclientService, deviceID, "") + restconfUnMountDevice(webclientService, deviceID) } } catch (bpe: BlueprintProcessorException) { log.error("Error looking up server identifier ", bpe) diff --git a/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfConstants.kt b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfConstants.kt new file mode 100644 index 000000000..2c52b52a0 --- /dev/null +++ b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfConstants.kt @@ -0,0 +1,21 @@ +package org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor + +class RestconfConstants { + companion object { + const val NODE_ID = "node-id" + const val FAIL_FAST = "fail-fast" + const val RESTCONF_CONNECTION_CONFIG = "restconf-connection-config" + const val MOUNT_PAYLOAD = "mount-payload" + const val ACTION_INPUT = "action-input" + const val ACTION_OUTPUT = "action-output" + const val ACTION_TYPE = "action-type" + const val ACTION_DATASTORE = "action-datastore" + const val ACTION_PATH = "action-path" + const val ACTION_PAYLOAD = "action-payload" + const val RESTCONF_TOPOLOGY_CONFIG_PATH = + "/restconf/config/network-topology:network-topology/topology/topology-netconf/node" + const val RESTCONF_TOPOLOGY_OPER_PATH = + "/restconf/operational/network-topology:network-topology/topology/topology-netconf/node" + val HTTP_SUCCESS_RANGE = 200..204 + } +} diff --git a/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutor.kt b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutor.kt new file mode 100644 index 000000000..f9c9ce88d --- /dev/null +++ b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutor.kt @@ -0,0 +1,198 @@ +package org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.node.TextNode +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.ACTION_DATASTORE +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.ACTION_INPUT +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.ACTION_OUTPUT +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.ACTION_PATH +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.ACTION_PAYLOAD +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.ACTION_TYPE +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.NODE_ID +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.FAIL_FAST +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.HTTP_SUCCESS_RANGE +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.MOUNT_PAYLOAD +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.RESTCONF_CONNECTION_CONFIG +import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService +import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction +import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.ComponentRemoteScriptExecutor +import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.ComponentScriptExecutor +import org.onap.ccsdk.cds.controllerblueprints.core.BlueprintConstants +import org.onap.ccsdk.cds.controllerblueprints.core.BlueprintConstants.PROPERTY_CONNECTION_CONFIG +import org.onap.ccsdk.cds.controllerblueprints.core.BlueprintProcessorException +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType +import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils.Companion.jsonNodeFromObject +import org.slf4j.LoggerFactory + +open class Mount : AbstractScriptComponentFunction() { + + val log = LoggerFactory.getLogger(Mount::class.java)!! + + override fun getName(): String { + return "Mount" + } + + override suspend fun processNB(executionRequest: ExecutionServiceInput) { + log.info("Mounting ODL restconf node process") + + val deviceInformation = relationshipProperty(RESTCONF_CONNECTION_CONFIG, PROPERTY_CONNECTION_CONFIG) + val webclientService = restconfClientService(deviceInformation) + + val nodeId = requestPayloadActionProperty(NODE_ID)?.first()?.textValue() + ?: throw BlueprintProcessorException("Failed to load $NODE_ID properties.") + val mountPayload = requestPayloadActionProperty(MOUNT_PAYLOAD)?.first() + ?: throw BlueprintProcessorException("Failed to load $MOUNT_PAYLOAD properties.") + restconfMountDeviceJson(webclientService, nodeId, mountPayload.toString()) + + setAttribute( + ComponentRemoteScriptExecutor.ATTRIBUTE_STATUS, + BlueprintConstants.STATUS_SUCCESS.asJsonPrimitive() + ) + } + + override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) { + addError("failed in restconf execution : ${runtimeException.message}") + } +} + +open class Execute : AbstractScriptComponentFunction() { + + val log = LoggerFactory.getLogger(Execute::class.java)!! + + override fun getName(): String { + return "Execute" + } + + override suspend fun processNB(executionRequest: ExecutionServiceInput) { + val nodeIdJson = requestPayloadActionProperty(NODE_ID)?.first() + ?: throw BlueprintProcessorException("Failed to load $NODE_ID properties.") + + val failFastJsonNode = requestPayloadActionProperty(FAIL_FAST)!! + val failFast = if (failFastJsonNode.isEmpty) false else failFastJsonNode.first().booleanValue() + + val deviceInformation = relationshipProperty(RESTCONF_CONNECTION_CONFIG, PROPERTY_CONNECTION_CONFIG) + + val webclientService = restconfClientService(deviceInformation) + val nodeId = nodeIdJson.textValue() + + val actionList = requestPayloadActionProperty("action")?.first() + ?: throw BlueprintProcessorException("Failed to load action properties.") + validateActionList(actionList) + + val actionListResults: MutableList<Map<String, JsonNode>> = mutableListOf() + + for (action in actionList) { + val actionTypeJsonNode = action.get(ACTION_TYPE) + val actionPathJsonNode = action.get(ACTION_PATH) + val actionType = RestconfRequestType.valueOf(actionTypeJsonNode.asText().toUpperCase()) + val dsJsonNode = action.get(ACTION_DATASTORE) + val path = restconfPath( + RestconfRequestDatastore.valueOf(dsJsonNode.asText().toUpperCase()), + nodeId, actionPathJsonNode.asText() + ) + val payload = action.get(ACTION_PAYLOAD) + + log.info("Processing Restconf action : $actionType $path" + if (payload != null) " $payload" else "") + val response = executeAction(webclientService, path, actionType, payload) + val responseBody = response.body + + val actionInput: MutableMap<String, JsonNode> = hashMapOf() + actionInput[ACTION_TYPE] = actionTypeJsonNode + actionInput[ACTION_PATH] = actionPathJsonNode + actionInput[ACTION_DATASTORE] = dsJsonNode + + val actionResult: MutableMap<String, JsonNode> = hashMapOf() + val actionResponse = responseBody.asJsonType() + if (actionResponse !is TextNode && actionResponse.toString().isNotBlank()) { + actionResult[ACTION_OUTPUT] = actionResponse + } + actionResult[ACTION_INPUT] = jsonNodeFromObject(actionInput) + + if (response.status in HTTP_SUCCESS_RANGE) { + log.info("\nRestconf execution response : \n{}", responseBody.asJsonType().toPrettyString()) + actionResult[ComponentScriptExecutor.ATTRIBUTE_STATUS] = + BlueprintConstants.STATUS_SUCCESS.asJsonPrimitive() + } else { + actionResult[ComponentScriptExecutor.ATTRIBUTE_STATUS] = + BlueprintConstants.STATUS_FAILURE.asJsonPrimitive() + addError( + BlueprintConstants.STATUS_FAILURE, + ComponentScriptExecutor.ATTRIBUTE_STATUS, + actionResponse.asText() + ) + if (failFast) { + actionListResults.add(actionResult) + break + } + } + actionListResults.add(actionResult) + } + + setAttribute(ComponentScriptExecutor.ATTRIBUTE_RESPONSE_DATA, jsonNodeFromObject(actionListResults)) + setAttribute(ComponentScriptExecutor.ATTRIBUTE_STATUS, BlueprintConstants.STATUS_SUCCESS.asJsonPrimitive()) + actionListResults.forEach { actionResult -> + val actionResultStatus = actionResult[ComponentScriptExecutor.ATTRIBUTE_STATUS] + if (BlueprintConstants.STATUS_SUCCESS.asJsonPrimitive() != actionResultStatus) { + setAttribute( + ComponentScriptExecutor.ATTRIBUTE_STATUS, + BlueprintConstants.STATUS_FAILURE.asJsonPrimitive() + ) + val errorResponse = actionResult[ACTION_OUTPUT]!! + addError( + BlueprintConstants.STATUS_FAILURE, ComponentScriptExecutor.ATTRIBUTE_STATUS, + errorResponse.asText() + ) + } + } + } + + override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) { + addError("failed in restconf execution : ${runtimeException.message}") + } + + private suspend fun executeAction( + webClientService: BlueprintWebClientService, + path: String, + actionType: RestconfRequestType, + payload: JsonNode? + ): BlueprintWebClientService.WebClientResponse<String> { + var headers = mutableMapOf("Content-Type" to "application/json") + return when (actionType) { + RestconfRequestType.PATCH -> { + headers = mutableMapOf("Content-Type" to "application/yang.patch-status+json") + genericPutPatchPostRequest(webClientService, path, actionType, payload.toString(), headers) + } + RestconfRequestType.PUT, RestconfRequestType.POST -> { + genericPutPatchPostRequest(webClientService, path, actionType, payload.toString(), headers) + } + RestconfRequestType.GET -> { + getRequest(webClientService, path) + } + RestconfRequestType.DELETE -> { + genericGetOrDeleteRequest(webClientService, path, RestconfRequestType.DELETE) + } + } + } + + private fun validateActionList(actionList: JsonNode) { + if (actionList.isEmpty) { + throw BlueprintProcessorException("No actions defined") + } + actionList.forEach { action -> + action.get(ACTION_PATH) + ?: throw BlueprintProcessorException("Failed to load action path.") + action.get(ACTION_DATASTORE) + ?: throw BlueprintProcessorException("Failed to load action datastore.") + val actionTypeJsonNode = action.get(ACTION_TYPE) + ?: throw BlueprintProcessorException("Failed to load action type.") + when (val actionType = RestconfRequestType.valueOf(actionTypeJsonNode.asText().toUpperCase())) { + RestconfRequestType.PATCH, RestconfRequestType.PUT, RestconfRequestType.POST -> { + action.get(ACTION_PAYLOAD) + ?: throw BlueprintProcessorException("Failed to load action $actionType payload.") + } + } + } + } +} diff --git a/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutorExtensions.kt b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutorExtensions.kt index 61ab4c11c..aade8965b 100644 --- a/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutorExtensions.kt +++ b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutorExtensions.kt @@ -17,7 +17,10 @@ package org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor +import com.fasterxml.jackson.databind.JsonNode import org.hibernate.annotations.common.util.impl.LoggerFactory +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.RESTCONF_TOPOLOGY_CONFIG_PATH +import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.RESTCONF_TOPOLOGY_OPER_PATH import org.onap.ccsdk.cds.blueprintsprocessor.rest.restClientService import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction @@ -28,44 +31,75 @@ import org.onap.ccsdk.cds.controllerblueprints.core.service.BlueprintDependencyS /** * Register the Restconf module exposed dependency */ - val log = LoggerFactory.logger(AbstractScriptComponentFunction::class.java)!! fun AbstractScriptComponentFunction.restconfClientService(selector: String): BlueprintWebClientService { return BlueprintDependencyService.restClientService(selector) } +fun AbstractScriptComponentFunction.restconfClientService(jsonNode: JsonNode): BlueprintWebClientService { + return BlueprintDependencyService.restClientService(jsonNode) +} + /** * Generic Mount function */ +suspend fun AbstractScriptComponentFunction.restconfMountDeviceJson( + webClientService: BlueprintWebClientService, + deviceId: String, + payload: Any +) { + restconfMountDevice(webClientService, deviceId, payload, mutableMapOf("Content-Type" to "application/json")) +} +/** + * Generic Mount function + */ suspend fun AbstractScriptComponentFunction.restconfMountDevice( webClientService: BlueprintWebClientService, deviceId: String, payload: Any, headers: Map<String, String> = mutableMapOf("Content-Type" to "application/xml") ) { + val mountUrl = restconfDeviceConfigPath(deviceId) + val mountCheckUrl = restconfDeviceOperPath(deviceId) + restconfMountDevice(webClientService, payload, mountUrl, mountCheckUrl, headers) +} - val mountUrl = "/restconf/config/network-topology:network-topology/topology/topology-netconf/node/$deviceId" +/** + * Generic Mount function + * This function mount the given deviceId and verify if device mounted successfully. + * This function take mount url and mount verify url as parameters. + */ +suspend fun AbstractScriptComponentFunction.restconfMountDevice( + webClientService: BlueprintWebClientService, + payload: Any, + mountUrl: String, + mountVerifyUrl: String, + headers: Map<String, String> = mutableMapOf("Content-Type" to "application/xml"), + expectedMountResult: String = """netconf-node-topology:connection-status":"connected""" +) { log.info("sending mount request, url: $mountUrl") - webClientService.exchangeResource("PUT", mountUrl, payload as String, headers) + log.debug("sending mount request, payload: $payload") + val mountResult = + webClientService.exchangeResource(RestconfRequestType.PUT.name, mountUrl, payload as String, headers) - /** Check device has mounted */ - val mountCheckUrl = "/restconf/operational/network-topology:network-topology/topology/topology-netconf/node/$deviceId" + if (mountResult.status !in RestconfConstants.HTTP_SUCCESS_RANGE) { + throw BlueprintProcessorException("Failed to mount device with url($mountUrl) ") + } - val expectedResult = """"netconf-node-topology:connection-status":"connected"""" - val mountCheckExecutionBlock: suspend (Int) -> String = { tryCount: Int -> - val result = webClientService.exchangeResource("GET", mountCheckUrl, "") - if (result.body.contains(expectedResult)) { - log.info("NF was mounted successfully on ODL") - result.body - } else { - throw BlueprintRetryException("Wait for device($deviceId) to mount") + /** Check device has mounted */ + val mountCheckExecutionBlock: suspend (Int) -> String = { + val result = webClientService.exchangeResource(RestconfRequestType.GET.name, mountVerifyUrl, "") + if (!result.body.contains(expectedMountResult)) { + throw BlueprintRetryException("Wait for device with url($mountUrl) to mount") } + log.info("NF was mounted successfully on ODL") + result.body } - log.info("url for ODL status check: $mountCheckUrl") - webClientService.retry<String>(10, 0, 1000, mountCheckExecutionBlock) + log.info("url for ODL status check: $mountVerifyUrl") + webClientService.retry(10, 0, 1000, mountCheckExecutionBlock) } /** @@ -81,35 +115,26 @@ suspend fun AbstractScriptComponentFunction.restconfApplyDeviceConfig( ): BlueprintWebClientService.WebClientResponse<String> { log.debug("headers: $additionalHeaders") log.info("configuring device: $deviceId, Configlet: $configletToApply") - val applyConfigUrl = "/restconf/config/network-topology:network-topology/topology/topology-netconf/node/" + - "$deviceId/$configletResourcePath" - return webClientService.exchangeResource("PATCH", applyConfigUrl, configletToApply as String, additionalHeaders) + val applyConfigUrl = restconfDeviceConfigPath(deviceId, configletResourcePath) + return webClientService.exchangeResource(RestconfRequestType.PATCH.name, applyConfigUrl, configletToApply as String, additionalHeaders) } suspend fun AbstractScriptComponentFunction.restconfDeviceConfig( webClientService: BlueprintWebClientService, deviceId: String, configletResourcePath: String -): - BlueprintWebClientService.WebClientResponse<String> { - - val configPathUrl = "/restconf/config/network-topology:network-topology/topology/topology-netconf/node/" + - "$deviceId/$configletResourcePath" - log.debug("sending GET request, url: $configPathUrl") - return webClientService.exchangeResource("GET", configPathUrl, "") - } +): BlueprintWebClientService.WebClientResponse<String> { + return getRequest(webClientService, restconfDeviceConfigPath(deviceId, configletResourcePath)) +} /** * Generic UnMount function */ suspend fun AbstractScriptComponentFunction.restconfUnMountDevice( webClientService: BlueprintWebClientService, - deviceId: String, - payload: String + deviceId: String ) { - val unMountUrl = "/restconf/config/network-topology:network-topology/topology/topology-netconf/node/$deviceId" - log.info("sending unMount request, url: $unMountUrl") - webClientService.exchangeResource("DELETE", unMountUrl, "") + deleteRequest(webClientService, restconfDeviceConfigPath(deviceId)) } /** @@ -118,65 +143,99 @@ suspend fun AbstractScriptComponentFunction.restconfUnMountDevice( suspend fun AbstractScriptComponentFunction.genericPutPatchPostRequest( webClientService: BlueprintWebClientService, requestUrl: String, - requestType: String, + requestType: RestconfRequestType, payload: Any, headers: Map<String, String> = mutableMapOf("Content-Type" to "application/xml") ): BlueprintWebClientService.WebClientResponse<String> { - when (requestType.toUpperCase()) { - "PUT" -> log.info("sending PUT request, url: $requestUrl") - "PATCH" -> log.info("sending PATCH request, url: $requestUrl") - "POST" -> log.info("sending POST request, url: $requestUrl") + when (requestType) { + RestconfRequestType.PUT -> log.info("sending PUT request, url: $requestUrl") + RestconfRequestType.PATCH -> log.info("sending PATCH request, url: $requestUrl") + RestconfRequestType.POST -> log.info("sending POST request, url: $requestUrl") else -> throw BlueprintProcessorException("Illegal request type, only POST, PUT or PATCH allowed.") } - return webClientService.exchangeResource(requestType, requestUrl, payload as String, headers) + return webClientService.exchangeResource(requestType.name, requestUrl, payload as String, headers) } /** - * Generic GET/DELETE request function + * GET request function */ - -suspend fun AbstractScriptComponentFunction.genericGetOrDeleteRequest( +suspend fun AbstractScriptComponentFunction.getRequest( webClientService: BlueprintWebClientService, - requestUrl: String, - requestType: String + requestUrl: String ): BlueprintWebClientService.WebClientResponse<String> { - when (requestType.toUpperCase()) { - "GET" -> log.info("sending GET request, url: $requestUrl") - "DELETE" -> log.info("sending DELETE request, url: $requestUrl") - else -> throw BlueprintProcessorException("Illegal request type, only GET and DELETE allowed.") - } - return webClientService.exchangeResource(requestType, requestUrl, "") + val retryTimes = 10 + val mountCheckExecutionBlock: suspend (Int) -> BlueprintWebClientService.WebClientResponse<String> = + { tryCount: Int -> + val result = genericGetOrDeleteRequest(webClientService, requestUrl, RestconfRequestType.GET) + if (result.status !in RestconfConstants.HTTP_SUCCESS_RANGE && tryCount < retryTimes - 1) { + throw BlueprintRetryException("Failed to read url($requestUrl) to mount") + } + log.info("NF was mounted successfully on ODL") + result + } + + return webClientService.retry(retryTimes, 0, 1000, mountCheckExecutionBlock) } /** - * Generic Mount function - * This function mount the given deviceId and verify if device mounted successfully. - * This function take mount url and mount verify url as parameters. + * DELETE request function */ - -suspend fun AbstractScriptComponentFunction.restconfMountDevice( +suspend fun AbstractScriptComponentFunction.deleteRequest( webClientService: BlueprintWebClientService, - payload: Any, - mountUrl: String, - mountVerifyUrl: String, - headers: Map<String, String> = mutableMapOf("Content-Type" to "application/xml"), - expectedMountResult: String = """"netconf-node-topology:connection-status":"connected"""" -) { + requestUrl: String +): BlueprintWebClientService.WebClientResponse<String> { + return genericGetOrDeleteRequest(webClientService, requestUrl, RestconfRequestType.DELETE) +} - log.info("sending mount request, url: $mountUrl") - webClientService.exchangeResource("PUT", mountUrl, payload as String, headers) +/** + * Generic GET/DELETE request function + */ +suspend fun AbstractScriptComponentFunction.genericGetOrDeleteRequest( + webClientService: BlueprintWebClientService, + requestUrl: String, + requestType: RestconfRequestType +): BlueprintWebClientService.WebClientResponse<String> { + when (requestType) { + RestconfRequestType.GET -> log.info("sending GET request, url: $requestUrl") + RestconfRequestType.DELETE -> log.info("sending DELETE request, url: $requestUrl") + else -> throw BlueprintProcessorException("Illegal request type, only GET and DELETE allowed.") + } + return webClientService.exchangeResource(requestType.name, requestUrl, "") +} - /** Check device has mounted */ - val mountCheckExecutionBlock: suspend (Int) -> String = { tryCount: Int -> - val result = webClientService.exchangeResource("GET", mountVerifyUrl, "") - if (result.body.contains(expectedMountResult)) { - log.info("NF was mounted successfully on ODL") - result.body - } else { - throw BlueprintRetryException("Wait for device with url($mountUrl) to mount") +suspend fun AbstractScriptComponentFunction.restconfPath( + restconfDatastore: RestconfRequestDatastore, + deviceId: String, + specificPath: String = "" +): String { + return when (restconfDatastore) { + RestconfRequestDatastore.OPERATIONAL -> { + restconfDeviceOperPath(deviceId, specificPath) + } + RestconfRequestDatastore.CONFIG -> { + restconfDeviceConfigPath(deviceId, specificPath) } } +} - log.info("url for ODL status check: $mountVerifyUrl") - webClientService.retry<String>(10, 0, 1000, mountCheckExecutionBlock) +private fun AbstractScriptComponentFunction.restconfDeviceConfigPath( + deviceId: String, + specificPath: String = "" +): String { + val configPath = "$RESTCONF_TOPOLOGY_CONFIG_PATH/$deviceId" + if (specificPath.isBlank()) { + return configPath + } + return "$configPath/$specificPath" +} + +private fun AbstractScriptComponentFunction.restconfDeviceOperPath( + deviceId: String, + specificPath: String = "" +): String { + val operPath = "$RESTCONF_TOPOLOGY_OPER_PATH/$deviceId" + if (specificPath.isBlank()) { + return operPath + } + return "$operPath/$specificPath" } diff --git a/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfRequestType.kt b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfRequestType.kt new file mode 100644 index 000000000..277f97814 --- /dev/null +++ b/ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfRequestType.kt @@ -0,0 +1,14 @@ +package org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor + +enum class RestconfRequestType { + PUT, + PATCH, + POST, + GET, + DELETE +} + +enum class RestconfRequestDatastore { + CONFIG, + OPERATIONAL +} |