Updated for Jakarta Release
[ccsdk/cds.git] / components / model-catalog / blueprint-model / service-blueprint / 5GC_Simulator_CNF_CDS / Scripts / kotlin / KotlinK8sUpdateConfig.kt
1 /*\r
2 *  Copyright © 2019 TechMahindra\r
3 *\r
4 *  Licensed under the Apache License, Version 2.0 (the "License");\r
5 *  you may not use this file except in compliance with the License.\r
6 *  You may obtain a copy of the License at\r
7 *\r
8 *      http://www.apache.org/licenses/LICENSE-2.0\r
9 *\r
10 *  Unless required by applicable law or agreed to in writing, software\r
11 *  distributed under the License is distributed on an "AS IS" BASIS,\r
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13 *  See the License for the specific language governing permissions and\r
14 *  limitations under the License.\r
15 */\r
16 \r
17 package org.onap.ccsdk.cds.blueprintsprocessor.services.execution.scripts\r
18 \r
19 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput\r
20 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction\r
21 import org.slf4j.LoggerFactory\r
22 import com.fasterxml.jackson.databind.node.ObjectNode\r
23 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils\r
24 import com.fasterxml.jackson.databind.node.ArrayNode\r
25 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService\r
26 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException\r
27 import java.nio.file.Path\r
28 import org.springframework.http.HttpMethod\r
29 import org.onap.ccsdk.cds.blueprintsprocessor.rest.BasicAuthRestClientProperties\r
30 import org.springframework.http.HttpHeaders\r
31 import org.springframework.http.MediaType\r
32 import org.apache.http.message.BasicHeader\r
33 import java.util.Base64\r
34 import java.nio.charset.Charset\r
35 import java.io.IOException\r
36 import org.apache.http.client.methods.HttpUriRequest\r
37 import com.fasterxml.jackson.annotation.JsonProperty\r
38 import org.apache.commons.io.IOUtils\r
39 import org.apache.http.client.methods.HttpPost\r
40 import org.apache.http.client.entity.EntityBuilder\r
41 import java.nio.file.Files\r
42 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.RestLoggerService\r
43 import org.apache.http.client.ClientProtocolException\r
44 import com.fasterxml.jackson.databind.ObjectMapper\r
45 import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.definition.K8sPluginDefinitionApi\r
46 import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.K8sConnectionPluginConfiguration\r
47 import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.definition.K8sDefinitionRestClient\r
48 import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.instance.K8sConfigValueRequest\r
49 import org.onap.ccsdk.cds.blueprintsprocessor.functions.k8s.instance.K8sPluginInstanceApi\r
50 import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService\r
51 import com.fasterxml.jackson.dataformat.yaml.YAMLFactory\r
52 import com.fasterxml.jackson.module.kotlin.convertValue\r
53 \r
54 import com.google.gson.Gson\r
55 import com.google.gson.reflect.TypeToken\r
56 \r
57 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.storedContentFromResolvedArtifactNB\r
58 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BasicAuthRestClientService\r
59 \r
60 open class KotlinK8sUpdateConfig : AbstractScriptComponentFunction() {\r
61 \r
62     private val log = LoggerFactory.getLogger(KotlinK8sUpdateConfig::class.java)!!\r
63 \r
64     override fun getName(): String {\r
65         return "KotlinK8sUpdateConfig"\r
66     }\r
67 \r
68     override suspend fun processNB(executionRequest: ExecutionServiceInput) {\r
69 \r
70         println("Exeuting processNB")\r
71         log.info("Executing processNB from Kotlin script: KotlinK8sUpdateConfig ...")\r
72                 val bluePrintPropertiesService: BluePrintPropertiesService =this.functionDependencyInstanceAsType("bluePrintPropertiesService")\r
73 \r
74         // read the config  input\r
75         val baseK8sApiUrl = getDynamicProperties("api-access").get("url").asText()\r
76         val k8sApiUsername = getDynamicProperties("api-access").get("username").asText()\r
77         val k8sApiPassword = getDynamicProperties("api-access").get("password").asText()\r
78 \r
79         val prefix = "baseconfigput"\r
80 \r
81         val aaiApiUrl = getDynamicProperties("aai-access").get("url").asText()\r
82         val aaiApiUsername = getDynamicProperties("aai-access").get("username").asText()\r
83         val aaiApiPassword = getDynamicProperties("aai-access").get("password").asText()\r
84 \r
85         log.info("AAI params $aaiApiUrl")\r
86 \r
87         val resolution_key = getDynamicProperties("resolution-key").asText()\r
88 \r
89         val payload = storedContentFromResolvedArtifactNB(resolution_key, prefix)\r
90 \r
91         val payloadObject = JacksonUtils.jsonNode(payload) as ObjectNode\r
92 \r
93         val serviceInstanceID: String = getResolvedParameter(payloadObject, "service-instance-id")\r
94         val vnfID: String = getResolvedParameter(payloadObject, "vnf-id")\r
95 \r
96         log.info("Get serviceInstanceID $serviceInstanceID")\r
97         log.info("Get vnfID $vnfID")\r
98 \r
99         val vnfUrl = aaiApiUrl + "/aai/v19/network/generic-vnfs/generic-vnf/" + vnfID + "/vf-modules";\r
100 \r
101         val mapOfHeaders = hashMapOf<String, String>()\r
102         mapOfHeaders.put("Accept", "application/json")\r
103         mapOfHeaders.put("Content-Type", "application/json")\r
104         mapOfHeaders.put("x-FromAppId", "SO")\r
105         mapOfHeaders.put("X-TransactionId", "get_aai_subscr")\r
106         val basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()\r
107         basicAuthRestClientProperties.username = aaiApiUsername\r
108         basicAuthRestClientProperties.password = aaiApiPassword\r
109         basicAuthRestClientProperties.url = vnfUrl\r
110         basicAuthRestClientProperties.additionalHeaders =mapOfHeaders\r
111         val basicAuthRestClientService: BasicAuthRestClientService= BasicAuthRestClientService(basicAuthRestClientProperties)\r
112         try {\r
113             val resultOfGet: BlueprintWebClientService.WebClientResponse<String> = basicAuthRestClientService.exchangeResource(HttpMethod.GET.name, "", "")\r
114 \r
115             val aaiBody = resultOfGet.body\r
116             val aaiPayloadObject = JacksonUtils.jsonNode(aaiBody) as ObjectNode\r
117                         \r
118                         log.info("aaiPayloadObject: $aaiPayloadObject")\r
119 \r
120             for (item in aaiPayloadObject.get("vf-module")) {\r
121 \r
122                 log.info("item payload Deatils : $item")\r
123 \r
124                 val isItBaseVfModule = item.get("is-base-vf-module").asText()\r
125 \r
126                 if(isItBaseVfModule.toBoolean())\r
127                     continue\r
128 \r
129                 val vfModuleID: String = item.get("vf-module-id").asText()\r
130 \r
131                 log.info("AAI Vf-module ID is : $vfModuleID")\r
132 \r
133                 val vfModuleModelInvariantUuid: String = item.get("model-invariant-id").asText()\r
134 \r
135                 log.info("AAI Vf-module Invariant ID is : $vfModuleModelInvariantUuid")\r
136 \r
137                 val vfModuleModelUuid: String = item.get("model-version-id").asText()\r
138 \r
139                 log.info("AAI Vf-module UUID is : $vfModuleModelUuid")\r
140                                 \r
141                             val vfModuleCustUuid: String = item.get("model-customization-id").asText()\r
142 \r
143                 log.info("AAI Vf-module Customization UUID is : $vfModuleCustUuid")\r
144                         \r
145 \r
146                 val vfModuleInstance: String = item.get("heat-stack-id").asText()\r
147 \r
148                 log.info("AAI Vf-module Heat Stack ID : $vfModuleInstance")\r
149 \r
150                 val profileName: String = "profile-"+ vfModuleID\r
151                 val templateName: String = "template_" + vfModuleCustUuid\r
152 \r
153                 //val randomString = getRandomString(6)\r
154                 val configName: String = "config_"+ vfModuleID\r
155                                 \r
156                                 log.info("payloadObject: $payloadObject")\r
157 \r
158                 var supportedNssai: String = getResolvedParameter(payloadObject, "supportedNssai")\r
159 \r
160                 log.info("supportedNssai from SO -> "+ supportedNssai)\r
161                 log.info("configName ->"+ configName)\r
162                 log.info("profileName ->"+ profileName)\r
163                 log.info("templateName ->"+ templateName)\r
164 \r
165 \r
166                 executeK8sAPI(bluePrintPropertiesService,supportedNssai, k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleModelInvariantUuid, vfModuleCustUuid, templateName, configName, profileName, vfModuleInstance)\r
167 \r
168             }\r
169         }\r
170         catch (e: Exception) {\r
171             log.info("Caught exception trying to set config values!!")\r
172             throw BluePrintProcessorException("${e.message}")\r
173         }\r
174     }\r
175 \r
176     fun getRandomString(length: Int) : String {\r
177         val charset = "0123456789"\r
178         return (1..length)\r
179                 .map { charset.random() }\r
180                 .joinToString("")\r
181     }\r
182 \r
183     fun executeK8sAPI(bluePrintPropertiesService: BluePrintPropertiesService,supportedNssai: String, k8sApiUsername:String, k8sApiPassword:String, baseK8sApiUrl:String, vfModuleModelInvariantUuid:String, vfModuleCustUuid: String, templateName: String, configName:String, profileName:String, instanceId: String){\r
184 \r
185         println("Executing executeK8sAPI ...")\r
186 \r
187         // read and convert supportedNssai parameters from string to json\r
188         val sNssaiAsJsonObj = parseSupportedNssai(supportedNssai)\r
189 \r
190         // contruct config api\r
191         val api = K8sConfigApi(k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleModelInvariantUuid, vfModuleCustUuid, instanceId, bluePrintPropertiesService)\r
192 \r
193 \r
194         // invoke config api\r
195         var config = K8sConfigPayloadJson()\r
196         config.templateName = templateName\r
197         config.configName = configName\r
198         config.values = Config()\r
199         config.values.supportedNssai = SupportedNssai()\r
200         config.values.supportedNssai.snssaiInitial = SnssaiInitial()\r
201         config.values.supportedNssai.snssaiInitial.snssaiSecond = SnssaiSecond()\r
202         config.values.supportedNssai.snssaiInitial.snssaiSecond.snssaiFinalArray = Array<SnssaiFinal>(sNssaiAsJsonObj.size){i-> SnssaiFinal()}\r
203 \r
204         val dest = buildSNssaiArray(config.values.supportedNssai.snssaiInitial.snssaiSecond.snssaiFinalArray, sNssaiAsJsonObj)\r
205         api.createOrUpdateConfig(config, profileName, instanceId, configName, templateName)\r
206 \r
207         log.info("K8s Configurations create or update Completed")\r
208 \r
209     }\r
210 \r
211     fun buildSNssaiArray(payloadSnssai: Array<SnssaiFinal>, requestSnssai: Array<SnssaiFinal>): Array<SnssaiFinal>{\r
212 \r
213         System.arraycopy(requestSnssai, 0, payloadSnssai, 0, requestSnssai.size)\r
214 \r
215         return payloadSnssai\r
216 \r
217     }\r
218 \r
219     fun parseSupportedNssai(supportedNssai: String): Array<SnssaiFinal>{\r
220 \r
221         log.info("parsing supportedNssai string..")\r
222 \r
223         log.info("sNssai value from input..  $supportedNssai")\r
224 \r
225         val trimmed_supportedNssai = supportedNssai.replace("\\s".toRegex(), "").replace("\\r\\n","").replace("\\","")\r
226 \r
227         val gson = Gson()\r
228 \r
229         val startInd = trimmed_supportedNssai.indexOf('[')\r
230         val endInd = trimmed_supportedNssai.indexOf(']')\r
231 \r
232         val subStr = trimmed_supportedNssai.substring(startInd, endInd+1)\r
233 \r
234         val snType = object : TypeToken<Array<SnssaiFinal>>() {}.type\r
235 \r
236         var snList: Array<SnssaiFinal> = gson.fromJson(subStr, snType)\r
237 \r
238         log.info("parsing is done.")\r
239 \r
240         return snList\r
241 \r
242     }\r
243 \r
244     fun getResolvedParameter(payload: ObjectNode, keyName: String): String {\r
245         for (node in payload.get("resource-accumulator-resolved-data").elements()) {\r
246             if (node.get("param-name").asText().equals(keyName)) {\r
247                 return node.get("param-value").asText()\r
248             }\r
249         }\r
250         return ""\r
251     }\r
252 \r
253     fun getTemplatePrefixList(executionRequest: ExecutionServiceInput): ArrayList<String> {\r
254         val result = ArrayList<String>()\r
255         for (prefix in executionRequest.payload.get("resource-assignment-request").get("template-prefix").elements())\r
256             result.add(prefix.asText())\r
257         return result\r
258     }\r
259 \r
260     override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {\r
261         log.info("Recover function called!")\r
262         log.info("Execution request : $executionRequest")\r
263         log.error("Exception", runtimeException)\r
264         addError(runtimeException.message!!)\r
265     }\r
266 \r
267     inner class K8sConfigApi(\r
268             val username: String,\r
269             val password: String,\r
270             val baseUrl: String,\r
271             val definition: String,\r
272             val definitionVersion: String,\r
273                         val instanceId: String,\r
274                         val bluePrintPropertiesService: BluePrintPropertiesService\r
275                         \r
276     ) {\r
277         private val service: UploadFileConfigClientService // BasicAuthRestClientService\r
278 \r
279         init {\r
280             var mapOfHeaders = hashMapOf<String, String>()\r
281             mapOfHeaders.put("Accept", "application/json")\r
282             mapOfHeaders.put("Content-Type", "application/json")\r
283             mapOfHeaders.put("cache-control", " no-cache")\r
284             mapOfHeaders.put("Accept", "application/json")\r
285             var basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()\r
286             basicAuthRestClientProperties.username = username\r
287             basicAuthRestClientProperties.password = password\r
288             basicAuthRestClientProperties.url = "$baseUrl/v1/instance"\r
289             basicAuthRestClientProperties.additionalHeaders = mapOfHeaders\r
290 \r
291             this.service = UploadFileConfigClientService(basicAuthRestClientProperties)\r
292         }\r
293 \r
294         fun createOrUpdateConfig(configJson: K8sConfigPayloadJson, profileName: String, instanceId: String, configName: String, templateName: String) {\r
295             val objectMapper = ObjectMapper()\r
296                          var obj: Any? = null\r
297              val yamlReader = ObjectMapper(YAMLFactory())\r
298 \r
299             for(snssai in configJson.values.supportedNssai.snssaiInitial.snssaiSecond.snssaiFinalArray){\r
300                 println("snssai->" +snssai.snssai)\r
301                 println("status->"+snssai.status)\r
302 \r
303             }\r
304 \r
305             val configJsonString: String = objectMapper.writeValueAsString(configJson.values)\r
306 \r
307             log.info("payload generated -> "+ configJsonString)\r
308 \r
309            val startInd = configJsonString.indexOf('[')\r
310            val endInd = configJsonString.indexOf(']')\r
311 \r
312             val snssaiArray: String = configJsonString.substring(startInd, endInd+1).replace("\"","\\\"").replace("[","\"[").replace("]","]\"")\r
313 \r
314             val finalPayload: String = configJsonString.replaceRange(startInd..endInd, snssaiArray)\r
315 \r
316             log.info("payload restructured -> "+ finalPayload)\r
317                         obj = yamlReader.readValue(finalPayload, Any::class.java)\r
318                         \r
319 \r
320                                  \r
321                                 val api = K8sPluginInstanceApi(K8sConnectionPluginConfiguration(bluePrintPropertiesService))\r
322                \r
323                 val configValueRequest = K8sConfigValueRequest()\r
324                 configValueRequest.templateName = configJson.templateName\r
325                 configValueRequest.configName = configJson.configName\r
326                 configValueRequest.values = objectMapper.convertValue(obj)\r
327                     if (api.hasConfigurationValues(instanceId, configName)) {\r
328                 api.editConfigurationValues(configValueRequest, instanceId, configName)\r
329             } else {\r
330                 api.createConfigurationValues(configValueRequest, instanceId)\r
331             }\r
332         }\r
333 \r
334     }\r
335 }\r
336 \r
337 class UploadFileConfigClientService(\r
338         private val restClientProperties:\r
339         BasicAuthRestClientProperties\r
340 ) : BlueprintWebClientService {\r
341 \r
342     override fun defaultHeaders(): Map<String, String> {\r
343 \r
344         val encodedCredentials = setBasicAuth(\r
345                 restClientProperties.username,\r
346                 restClientProperties.password\r
347         )\r
348         return mapOf(\r
349                 HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,\r
350                 HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,\r
351                 HttpHeaders.AUTHORIZATION to "Basic $encodedCredentials"\r
352         )\r
353     }\r
354 \r
355     override fun host(uri: String): String {\r
356         return restClientProperties.url + uri\r
357     }\r
358 \r
359     override fun convertToBasicHeaders(headers: Map<String, String>):\r
360             Array<BasicHeader> {\r
361         val customHeaders: MutableMap<String, String> = headers.toMutableMap()\r
362         // inject additionalHeaders\r
363         customHeaders.putAll(verifyAdditionalHeaders(restClientProperties))\r
364 \r
365         if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {\r
366             val encodedCredentials = setBasicAuth(\r
367                     restClientProperties.username,\r
368                     restClientProperties.password\r
369             )\r
370             customHeaders[HttpHeaders.AUTHORIZATION] =\r
371                     "Basic $encodedCredentials"\r
372         }\r
373         return super.convertToBasicHeaders(customHeaders)\r
374     }\r
375 \r
376     private fun setBasicAuth(username: String, password: String): String {\r
377         val credentialsString = "$username:$password"\r
378         return Base64.getEncoder().encodeToString(\r
379                 credentialsString.toByteArray(Charset.defaultCharset())\r
380         )\r
381     }\r
382 \r
383     @Throws(IOException::class, ClientProtocolException::class)\r
384     private fun performHttpCall(httpUriRequest: HttpUriRequest): BlueprintWebClientService.WebClientResponse<String> {\r
385         val httpResponse = httpClient().execute(httpUriRequest)\r
386         val statusCode = httpResponse.statusLine.statusCode\r
387         httpResponse.entity.content.use {\r
388             val body = IOUtils.toString(it, Charset.defaultCharset())\r
389             return BlueprintWebClientService.WebClientResponse(statusCode, body)\r
390         }\r
391     }\r
392 \r
393     fun uploadBinaryFile(path: String, filePath: Path): BlueprintWebClientService.WebClientResponse<String> {\r
394         val convertedHeaders: Array<BasicHeader> = convertToBasicHeaders(defaultHeaders())\r
395         val httpPost = HttpPost(host(path))\r
396         val entity = EntityBuilder.create().setBinary(Files.readAllBytes(filePath)).build()\r
397         httpPost.setEntity(entity)\r
398         RestLoggerService.httpInvoking(convertedHeaders)\r
399         httpPost.setHeaders(convertedHeaders)\r
400         return performHttpCall(httpPost)\r
401     }\r
402 }\r
403 \r
404 \r
405 class K8sConfigPayloadJson {\r
406     @get:JsonProperty("template-name")\r
407     var templateName: String? = null\r
408     @get:JsonProperty("config-name")\r
409     var configName: String? = null\r
410     @get:JsonProperty("values")\r
411     lateinit var values: Config\r
412 \r
413     override fun toString(): String {\r
414         return "$templateName:$configName:$values"\r
415     }\r
416 \r
417     override fun equals(other: Any?): Boolean {\r
418         if (this === other) return true\r
419         if (javaClass != other?.javaClass) return false\r
420         return true\r
421     }\r
422 \r
423     override fun hashCode(): Int {\r
424         return javaClass.hashCode()\r
425     }\r
426 }\r
427 \r
428 class Config{\r
429     @get:JsonProperty("config")\r
430     lateinit var supportedNssai: SupportedNssai\r
431 }\r
432 \r
433 class SupportedNssai{\r
434     @get:JsonProperty("supportedNssai")\r
435     lateinit var snssaiInitial: SnssaiInitial\r
436 }\r
437 \r
438 class SnssaiInitial{\r
439 \r
440     @get:JsonProperty("sNssai")\r
441     lateinit var snssaiSecond: SnssaiSecond\r
442 }\r
443 \r
444 class SnssaiSecond{\r
445 \r
446     @get:JsonProperty("snssai")\r
447     lateinit var snssaiFinalArray: Array<SnssaiFinal>\r
448 }\r
449 \r
450 \r
451 class SnssaiFinal{\r
452     @get:JsonProperty("snssai")\r
453     var snssai: String? = null\r
454 \r
455     @get:JsonProperty("status")\r
456     var status: String? = null\r
457 }\r
458 \r
459 \r
460 fun main(args: Array<String>) {\r
461 \r
462     val supportedNssai = """\r
463 \r
464         {\r\n                                     \"sNssai\":[\r\n                                        {\r\n                                           \"snssai\":\"001-100001\",\r\n                                           \"status\":\"created-modified\"\r\n                                        },\r\n                                        {\r\n                                           \"snssai\":\"002-100001\",\r\n                                           \"status\":\"activated\"\r\n                                        },\r\n                                        {\r\n                                           \"snssai\":\"003-100001\",\r\n                                           \"status\":\"de-activated\"\r\n                                        }\r\n                                     ]\r\n                                  }\r\n\r
465 \r
466 """\r
467 \r
468     val kotlin = KotlinK8sUpdateConfig()\r
469 \r
470    \r
471 \r
472 }\r