Revert "Renaming Files having BluePrint to have Blueprint"
[ccsdk/cds.git] / ms / blueprintsprocessor / functions / restconf-executor / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / functions / restconf / executor / RestconfExecutorExtensions.kt
1 /*
2  *  Copyright © 2019 IBM.
3  *  Modifications Copyright © 2018-2019 IBM, Bell Canada
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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  */
17
18 package org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor
19
20 import com.fasterxml.jackson.databind.JsonNode
21 import org.hibernate.annotations.common.util.impl.LoggerFactory
22 import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.RESTCONF_TOPOLOGY_CONFIG_PATH
23 import org.onap.ccsdk.cds.blueprintsprocessor.functions.restconf.executor.RestconfConstants.Companion.RESTCONF_TOPOLOGY_OPER_PATH
24 import org.onap.ccsdk.cds.blueprintsprocessor.rest.restClientService
25 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
26 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
27 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
28 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintRetryException
29 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
30
31 /**
32  * Register the Restconf module exposed dependency
33  */
34 val log = LoggerFactory.logger(AbstractScriptComponentFunction::class.java)!!
35
36 fun AbstractScriptComponentFunction.restconfClientService(selector: String): BlueprintWebClientService {
37     return BluePrintDependencyService.restClientService(selector)
38 }
39
40 fun AbstractScriptComponentFunction.restconfClientService(jsonNode: JsonNode): BlueprintWebClientService {
41     return BluePrintDependencyService.restClientService(jsonNode)
42 }
43
44 /**
45  * Generic Mount function
46  */
47 suspend fun AbstractScriptComponentFunction.restconfMountDeviceJson(
48     webClientService: BlueprintWebClientService,
49     deviceId: String,
50     payload: Any
51 ) {
52     restconfMountDevice(webClientService, deviceId, payload, mutableMapOf("Content-Type" to "application/json"))
53 }
54
55 /**
56  * Generic Mount function
57  */
58 suspend fun AbstractScriptComponentFunction.restconfMountDevice(
59     webClientService: BlueprintWebClientService,
60     deviceId: String,
61     payload: Any,
62     headers: Map<String, String> = mutableMapOf("Content-Type" to "application/xml")
63 ) {
64     val mountUrl = restconfDeviceConfigPath(deviceId)
65     val mountCheckUrl = restconfDeviceOperPath(deviceId)
66     restconfMountDevice(webClientService, payload, mountUrl, mountCheckUrl, headers)
67 }
68
69 /**
70  * Generic Mount function
71  * This function mount the given deviceId and verify if device mounted successfully.
72  * This function take mount url and mount verify url as parameters.
73  */
74 suspend fun AbstractScriptComponentFunction.restconfMountDevice(
75     webClientService: BlueprintWebClientService,
76     payload: Any,
77     mountUrl: String,
78     mountVerifyUrl: String,
79     headers: Map<String, String> = mutableMapOf("Content-Type" to "application/xml"),
80     expectedMountResult: String = """netconf-node-topology:connection-status":"connected"""
81 ) {
82     log.info("sending mount request, url: $mountUrl")
83     log.debug("sending mount request, payload: $payload")
84     val mountResult =
85         webClientService.exchangeResource(RestconfRequestType.PUT.name, mountUrl, payload as String, headers)
86
87     if (mountResult.status !in RestconfConstants.HTTP_SUCCESS_RANGE) {
88         throw BluePrintProcessorException("Failed to mount device with url($mountUrl) ")
89     }
90
91     /** Check device has mounted */
92     val mountCheckExecutionBlock: suspend (Int) -> String = {
93         val result = webClientService.exchangeResource(RestconfRequestType.GET.name, mountVerifyUrl, "")
94         if (!result.body.contains(expectedMountResult)) {
95             throw BluePrintRetryException("Wait for device with url($mountUrl) to mount")
96         }
97         log.info("NF was mounted successfully on ODL")
98         result.body
99     }
100
101     log.info("url for ODL status check: $mountVerifyUrl")
102     webClientService.retry(10, 0, 1000, mountCheckExecutionBlock)
103 }
104
105 /**
106  * Generic Configure function
107  * @return The WebClientResponse from the request
108  */
109 suspend fun AbstractScriptComponentFunction.restconfApplyDeviceConfig(
110     webClientService: BlueprintWebClientService,
111     deviceId: String,
112     configletResourcePath: String,
113     configletToApply: Any,
114     additionalHeaders: Map<String, String> = mutableMapOf("Content-Type" to "application/yang.patch+xml")
115 ): BlueprintWebClientService.WebClientResponse<String> {
116     log.debug("headers: $additionalHeaders")
117     log.info("configuring device: $deviceId, Configlet: $configletToApply")
118     val applyConfigUrl = restconfDeviceConfigPath(deviceId, configletResourcePath)
119     return webClientService.exchangeResource(RestconfRequestType.PATCH.name, applyConfigUrl, configletToApply as String, additionalHeaders)
120 }
121
122 suspend fun AbstractScriptComponentFunction.restconfDeviceConfig(
123     webClientService: BlueprintWebClientService,
124     deviceId: String,
125     configletResourcePath: String
126 ): BlueprintWebClientService.WebClientResponse<String> {
127     return getRequest(webClientService, restconfDeviceConfigPath(deviceId, configletResourcePath))
128 }
129
130 /**
131  * Generic UnMount function
132  */
133 suspend fun AbstractScriptComponentFunction.restconfUnMountDevice(
134     webClientService: BlueprintWebClientService,
135     deviceId: String
136 ) {
137     deleteRequest(webClientService, restconfDeviceConfigPath(deviceId))
138 }
139
140 /**
141  * Generic PUT/PATCH/POST request function
142  */
143 suspend fun AbstractScriptComponentFunction.genericPutPatchPostRequest(
144     webClientService: BlueprintWebClientService,
145     requestUrl: String,
146     requestType: RestconfRequestType,
147     payload: Any,
148     headers: Map<String, String> = mutableMapOf("Content-Type" to "application/xml")
149 ): BlueprintWebClientService.WebClientResponse<String> {
150     when (requestType) {
151         RestconfRequestType.PUT -> log.info("sending PUT request, url: $requestUrl")
152         RestconfRequestType.PATCH -> log.info("sending PATCH request, url: $requestUrl")
153         RestconfRequestType.POST -> log.info("sending POST request, url: $requestUrl")
154         else -> throw BluePrintProcessorException("Illegal request type, only POST, PUT or PATCH allowed.")
155     }
156     return webClientService.exchangeResource(requestType.name, requestUrl, payload as String, headers)
157 }
158
159 /**
160  * GET request function
161  */
162 suspend fun AbstractScriptComponentFunction.getRequest(
163     webClientService: BlueprintWebClientService,
164     requestUrl: String
165 ): BlueprintWebClientService.WebClientResponse<String> {
166     val retryTimes = 10
167     val mountCheckExecutionBlock: suspend (Int) -> BlueprintWebClientService.WebClientResponse<String> =
168         { tryCount: Int ->
169             val result = genericGetOrDeleteRequest(webClientService, requestUrl, RestconfRequestType.GET)
170             if (result.status !in RestconfConstants.HTTP_SUCCESS_RANGE && tryCount < retryTimes - 1) {
171                 throw BluePrintRetryException("Failed to read url($requestUrl) to mount")
172             }
173             log.info("NF was mounted successfully on ODL")
174             result
175         }
176
177     return webClientService.retry(retryTimes, 0, 1000, mountCheckExecutionBlock)
178 }
179
180 /**
181  * DELETE request function
182  */
183 suspend fun AbstractScriptComponentFunction.deleteRequest(
184     webClientService: BlueprintWebClientService,
185     requestUrl: String
186 ): BlueprintWebClientService.WebClientResponse<String> {
187     return genericGetOrDeleteRequest(webClientService, requestUrl, RestconfRequestType.DELETE)
188 }
189
190 /**
191  * Generic GET/DELETE request function
192  */
193 suspend fun AbstractScriptComponentFunction.genericGetOrDeleteRequest(
194     webClientService: BlueprintWebClientService,
195     requestUrl: String,
196     requestType: RestconfRequestType
197 ): BlueprintWebClientService.WebClientResponse<String> {
198     when (requestType) {
199         RestconfRequestType.GET -> log.info("sending GET request, url: $requestUrl")
200         RestconfRequestType.DELETE -> log.info("sending DELETE request, url: $requestUrl")
201         else -> throw BluePrintProcessorException("Illegal request type, only GET and DELETE allowed.")
202     }
203     return webClientService.exchangeResource(requestType.name, requestUrl, "")
204 }
205
206 suspend fun AbstractScriptComponentFunction.restconfPath(
207     restconfDatastore: RestconfRequestDatastore,
208     deviceId: String,
209     specificPath: String = ""
210 ): String {
211     return when (restconfDatastore) {
212         RestconfRequestDatastore.OPERATIONAL -> {
213             restconfDeviceOperPath(deviceId, specificPath)
214         }
215         RestconfRequestDatastore.CONFIG -> {
216             restconfDeviceConfigPath(deviceId, specificPath)
217         }
218     }
219 }
220
221 private fun AbstractScriptComponentFunction.restconfDeviceConfigPath(
222     deviceId: String,
223     specificPath: String = ""
224 ): String {
225     val configPath = "$RESTCONF_TOPOLOGY_CONFIG_PATH/$deviceId"
226     if (specificPath.isBlank()) {
227         return configPath
228     }
229     return "$configPath/$specificPath"
230 }
231
232 private fun AbstractScriptComponentFunction.restconfDeviceOperPath(
233     deviceId: String,
234     specificPath: String = ""
235 ): String {
236     val operPath = "$RESTCONF_TOPOLOGY_OPER_PATH/$deviceId"
237     if (specificPath.isBlank()) {
238         return operPath
239     }
240     return "$operPath/$specificPath"
241 }