Extend Restconf executor function 52/119952/7
authorClaudio D. Gasparini <claudio.gasparini@intl.att.com>
Mon, 29 Mar 2021 08:02:17 +0000 (10:02 +0200)
committerClaudio David Gasparini <claudio.gasparini@intl.att.com>
Tue, 30 Mar 2021 14:56:08 +0000 (14:56 +0000)
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

components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfConfigDeploy.kt
components/model-catalog/blueprint-model/uat-blueprints/PNF_CDS_RESTCONF/Scripts/kotlin/RestconfSoftwareUpgrade.kt
components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Scripts/kotlin/RestconfConfigDeploy.kt
ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfConstants.kt [new file with mode: 0644]
ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutor.kt [new file with mode: 0644]
ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfExecutorExtensions.kt
ms/blueprintsprocessor/functions/restconf-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/restconf/executor/RestconfRequestType.kt [new file with mode: 0644]

index 78ab34e..d422b7d 100644 (file)
@@ -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)
index 96345f0..f2335c1 100644 (file)
@@ -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)
         }
     }
 
index 2ba527a..0396855 100644 (file)
@@ -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 (file)
index 0000000..2c52b52
--- /dev/null
@@ -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 (file)
index 0000000..f9c9ce8
--- /dev/null
@@ -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.")
+                }
+            }
+        }
+    }
+}
index 61ab4c1..aade896 100644 (file)
 
 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 (file)
index 0000000..277f978
--- /dev/null
@@ -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
+}