Correct CBA package for vFW_CNF_CDS usecase
[demo.git] / heat / vFW_CNF_CDS / templates / cba / Scripts / kotlin / KotlinK8sProfileUpload.kt
1 /*
2  * Copyright © 2019 Orange
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.ccsdk.cds.blueprintsprocessor.services.execution.scripts
18
19 import com.fasterxml.jackson.databind.node.ObjectNode
20 import com.fasterxml.jackson.databind.ObjectMapper
21 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
22 import org.onap.ccsdk.cds.blueprintsprocessor.rest.BasicAuthRestClientProperties
23 import org.onap.ccsdk.cds.blueprintsprocessor.rest.RestClientProperties
24 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BasicAuthRestClientService
25 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
26 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.RestLoggerService
27 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractScriptComponentFunction
28 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
29 import org.apache.commons.io.IOUtils
30 import org.apache.commons.io.FilenameUtils
31 import org.apache.http.client.entity.EntityBuilder
32 import org.apache.http.entity.ContentType
33 import org.apache.http.message.BasicHeader
34 import org.apache.http.client.methods.HttpPost
35 import org.apache.http.client.methods.HttpUriRequest
36 import org.apache.http.client.ClientProtocolException
37 import org.slf4j.LoggerFactory
38 import org.springframework.http.HttpMethod
39 import org.springframework.web.client.RestTemplate
40 import com.fasterxml.jackson.annotation.JsonIgnore
41 import com.fasterxml.jackson.annotation.JsonProperty
42 import java.util.ArrayList
43 import java.io.IOException
44 import java.io.File
45 import java.nio.file.Files
46 import java.nio.file.Paths
47 import java.nio.file.Path
48 import org.springframework.http.HttpHeaders
49 import org.springframework.http.MediaType
50 import java.nio.charset.Charset
51 import java.util.Base64
52 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
53
54 import java.io.BufferedInputStream
55 import java.io.FileInputStream
56 import java.io.FileOutputStream
57 import java.util.zip.GZIPInputStream
58
59 open class K8sProfileUpload : AbstractScriptComponentFunction() {
60
61     private val log = LoggerFactory.getLogger(K8sProfileUpload::class.java)!!
62
63     override fun getName(): String {
64         return "K8sProfileUpload"
65     }
66
67     override suspend fun processNB(executionRequest: ExecutionServiceInput) {
68         log.info("executing K8s Profile Upload script")
69
70         val baseK8sApiUrl = getDynamicProperties("api-access").get("url").asText()
71         val k8sApiUsername = getDynamicProperties("api-access").get("username").asText()
72         val k8sApiPassword = getDynamicProperties("api-access").get("password").asText()
73
74         val prefixList: ArrayList<String> = getTemplatePrefixList(executionRequest)
75
76         for (prefix in prefixList) {
77             if (prefix.toLowerCase().equals("vnf")) {
78                 log.info("For vnf-level resource-assignment, profile upload is not performed")
79                 continue
80             }
81
82             val assignmentParams = getDynamicProperties("assignment-params")
83             val payloadObject = JacksonUtils.jsonNode(assignmentParams.get(prefix).asText()) as ObjectNode
84
85             log.info("Uploading K8S profile for template prefix $prefix")
86
87             val vfModuleModelInvariantUuid: String = getResolvedParameter(payloadObject, "vf-module-model-invariant-uuid")
88             val vfModuleModelUuid: String = getResolvedParameter(payloadObject, "vf-module-model-version")
89             val k8sRbProfileName: String = getResolvedParameter(payloadObject, "k8s-rb-profile-name")
90             val k8sRbProfileNamespace: String = getResolvedParameter(payloadObject, "k8s-rb-profile-namespace")
91
92             val api = K8sApi(k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleModelInvariantUuid, vfModuleModelUuid)
93
94             if (!api.hasDefinition()) {
95                 throw BluePrintProcessorException("K8s RB Definition (${vfModuleModelInvariantUuid}/${vfModuleModelUuid}) not found ")
96             }
97
98             log.info("k8s-rb-profile-name: $k8sRbProfileName")
99             if (k8sRbProfileName.equals("")) {
100                 throw BluePrintProcessorException("K8s rb profile name is empty! Either define profile name to use or choose default")
101             }
102             if (k8sRbProfileName.equals("default") and api.hasProfile(k8sRbProfileName)) {
103                 log.info("Using default profile - skipping upload")
104             } else {
105                 if (api.hasProfile(k8sRbProfileName)) {
106                     log.info("Profile Already Existing - skipping upload")
107                 } else {
108                     val profileFilePath = prepareProfileFile(k8sRbProfileName)
109
110                     var profile = K8sProfile()
111                     profile.profileName = k8sRbProfileName
112                     profile.rbName = vfModuleModelInvariantUuid
113                     profile.rbVersion = vfModuleModelUuid
114                     profile.namespace = k8sRbProfileNamespace
115                     api.createProfile(profile)
116                     api.uploadProfileContent(profile, profileFilePath)
117
118                     log.info("K8s Profile Upload Completed")
119                 }
120             }
121         }
122     }
123
124     fun prepareProfileFile(k8sRbProfileName: String): Path {
125         val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
126         val bluePrintBasePath: String = bluePrintContext.rootPath
127         var profileFilePath: Path = Paths.get(bluePrintBasePath.plus(File.separator).plus("Templates").plus(File.separator).plus("k8s-profiles").plus(File.separator).plus("${k8sRbProfileName}.tar.gz"))
128         log.info("Reading K8s profile file: ${profileFilePath}")
129
130         val profileFile = profileFilePath.toFile()
131
132         if (!profileFile.exists())
133             throw BluePrintProcessorException("K8s Profile template file ${profileFilePath} does not exists")
134
135         return profileFilePath
136     }
137
138     fun getTemplatePrefixList(executionRequest: ExecutionServiceInput): ArrayList<String> {
139         val result = ArrayList<String>()
140         for (prefix in executionRequest.payload.get("resource-assignment-request").get("template-prefix").elements())
141             result.add(prefix.asText())
142         return result
143     }
144
145     fun getResolvedParameter(payload: ObjectNode, keyName: String): String {
146         for (node in payload.get("resource-accumulator-resolved-data").elements()) {
147             if (node.get("param-name").asText().equals(keyName)) {
148                 return node.get("param-value").asText()
149             }
150         }
151         return ""
152     }
153
154     override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
155         log.info("Executing Recovery")
156         bluePrintRuntimeService.getBluePrintError().addError("${runtimeException.message}")
157     }
158
159     inner class K8sApi(val username: String, val password: String, val baseUrl: String, val definition: String,
160                        val definitionVersion: String) {
161         private val service: UploadFileRestClientService //BasicAuthRestClientService
162
163         init {
164             var mapOfHeaders = hashMapOf<String, String>()
165             mapOfHeaders.put("Accept", "application/json")
166             mapOfHeaders.put("Content-Type", "application/json")
167             mapOfHeaders.put("cache-control", " no-cache")
168             mapOfHeaders.put("Accept", "application/json")
169             var basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
170             basicAuthRestClientProperties.username = username
171             basicAuthRestClientProperties.password = password
172             basicAuthRestClientProperties.url = "$baseUrl/v1/rb/definition/${definition}/${definitionVersion}"
173             basicAuthRestClientProperties.additionalHeaders = mapOfHeaders
174
175             this.service = UploadFileRestClientService(basicAuthRestClientProperties)
176         }
177
178         fun hasDefinition(): Boolean {
179             try {
180                 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name, "", "")
181                 print(result)
182                 if (result.status >= 200 && result.status < 300)
183                     return true
184                 else
185                     return false
186             } catch (e: Exception) {
187                 log.info("Caught exception trying to get k8s rb definition")
188                 throw BluePrintProcessorException("${e.message}")
189             }
190         }
191
192         fun hasProfile(profileName: String): Boolean {
193             try {
194                 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name,
195                         "/profile/$profileName", "")
196                 if (result.status >= 200 && result.status < 300)
197                     return true
198                 else {
199                     print(result)
200                     return false
201                 }
202             } catch (e: Exception) {
203                 log.info("Caught exception trying to get k8s rb profile")
204                 throw BluePrintProcessorException("${e.message}")
205             }
206         }
207
208         fun createProfile(profile: K8sProfile) {
209             val objectMapper = ObjectMapper()
210             val profileJsonString: String = objectMapper.writeValueAsString(profile)
211             try {
212                 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.POST.name,
213                         "/profile", profileJsonString)
214                 if (result.status < 200 || result.status >= 300) {
215                     throw Exception(result.body)
216                 }
217             } catch (e: Exception) {
218                 log.info("Caught exception trying to create k8s rb profile ${profile.profileName}")
219                 throw BluePrintProcessorException("${e.message}")
220             }
221         }
222
223         fun uploadProfileContent(profile: K8sProfile, filePath: Path) {
224             try {
225                 val result: BlueprintWebClientService.WebClientResponse<String> = service.uploadBinaryFile(
226                         "/profile/${profile.profileName}/content", filePath)
227                 if (result.status < 200 || result.status >= 300) {
228                     throw Exception(result.body)
229                 }
230             } catch (e: Exception) {
231                 log.info("Caught exception trying to upload k8s rb profile ${profile.profileName}")
232                 throw BluePrintProcessorException("${e.message}")
233             }
234         }
235     }
236 }
237
238 class UploadFileRestClientService(
239         private val restClientProperties:
240         BasicAuthRestClientProperties
241 ) : BlueprintWebClientService {
242
243     override fun defaultHeaders(): Map<String, String> {
244
245         val encodedCredentials = setBasicAuth(
246                 restClientProperties.username,
247                 restClientProperties.password
248         )
249         return mapOf(
250                 HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
251                 HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,
252                 HttpHeaders.AUTHORIZATION to "Basic $encodedCredentials"
253         )
254     }
255
256     override fun host(uri: String): String {
257         return restClientProperties.url + uri
258     }
259
260     override fun convertToBasicHeaders(headers: Map<String, String>):
261             Array<BasicHeader> {
262         val customHeaders: MutableMap<String, String> = headers.toMutableMap()
263         // inject additionalHeaders
264         customHeaders.putAll(verifyAdditionalHeaders(restClientProperties))
265
266         if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
267             val encodedCredentials = setBasicAuth(
268                     restClientProperties.username,
269                     restClientProperties.password
270             )
271             customHeaders[HttpHeaders.AUTHORIZATION] =
272                     "Basic $encodedCredentials"
273         }
274         return super.convertToBasicHeaders(customHeaders)
275     }
276
277     private fun setBasicAuth(username: String, password: String): String {
278         val credentialsString = "$username:$password"
279         return Base64.getEncoder().encodeToString(
280                 credentialsString.toByteArray(Charset.defaultCharset())
281         )
282     }
283
284     @Throws(IOException::class, ClientProtocolException::class)
285     private fun performHttpCall(httpUriRequest: HttpUriRequest): BlueprintWebClientService.WebClientResponse<String> {
286         val httpResponse = httpClient().execute(httpUriRequest)
287         val statusCode = httpResponse.statusLine.statusCode
288         httpResponse.entity.content.use {
289             val body = IOUtils.toString(it, Charset.defaultCharset())
290             return BlueprintWebClientService.WebClientResponse(statusCode, body)
291         }
292     }
293
294     fun uploadBinaryFile(path: String, filePath: Path): BlueprintWebClientService.WebClientResponse<String> {
295         val convertedHeaders: Array<BasicHeader> = convertToBasicHeaders(defaultHeaders())
296         val httpPost = HttpPost(host(path))
297         val entity = EntityBuilder.create().setBinary(Files.readAllBytes(filePath)).build()
298         httpPost.setEntity(entity)
299         RestLoggerService.httpInvoking(convertedHeaders)
300         httpPost.setHeaders(convertedHeaders)
301         return performHttpCall(httpPost)
302     }
303 }
304
305 class K8sProfile {
306     @get:JsonProperty("rb-name")
307     var rbName: String? = null
308     @get:JsonProperty("rb-version")
309     var rbVersion: String? = null
310     @get:JsonProperty("profile-name")
311     var profileName: String? = null
312     @get:JsonProperty("namespace")
313     var namespace: String? = "default"
314
315     override fun toString(): String {
316         return "$rbName:$rbVersion:$profileName"
317     }
318
319     override fun equals(other: Any?): Boolean {
320         if (this === other) return true
321         if (javaClass != other?.javaClass) return false
322         return true
323     }
324
325     override fun hashCode(): Int {
326         return javaClass.hashCode()
327     }
328 }