Removed resolution-key from K8S profile upload
[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                 continue
79
80             val assignmentParams = getDynamicProperties("assignment-params")
81             val payloadObject = JacksonUtils.jsonNode(assignmentParams.get(prefix).asText()) as ObjectNode
82
83             log.info("Uploading K8S profile for template prefix $prefix")
84
85             val vfModuleModelInvariantUuid: String = getResolvedParameter(payloadObject, "vf-module-model-invariant-uuid")
86             val vfModuleModelUuid: String = getResolvedParameter(payloadObject, "vf-module-model-version")
87             val k8sRbProfileName: String = getResolvedParameter(payloadObject, "k8s-rb-profile-name")
88             val k8sRbProfileNamespace: String = getResolvedParameter(payloadObject, "k8s-rb-profile-namespace")
89
90             val api = K8sApi(k8sApiUsername, k8sApiPassword, baseK8sApiUrl, vfModuleModelInvariantUuid, vfModuleModelUuid)
91
92             if (!api.hasDefinition()) {
93                 throw BluePrintProcessorException("K8s RB Definition (${vfModuleModelInvariantUuid}/${vfModuleModelUuid}) not found ")
94             }
95
96             log.info("k8s-rb-profile-name: $k8sRbProfileName")
97             if (k8sRbProfileName.equals("")) {
98                 log.info("Profile Name Not Defined - skipping upload")
99             } else {
100                 if (api.hasProfile(k8sRbProfileName)) {
101                     log.info("Profile Already Existing - skipping upload")
102                 } else {
103                     val profileFilePath = prepareProfileFile(k8sRbProfileName)
104
105                     var profile = K8sProfile()
106                     profile.profileName = k8sRbProfileName
107                     profile.rbName = vfModuleModelInvariantUuid
108                     profile.rbVersion = vfModuleModelUuid
109                     profile.namespace = k8sRbProfileNamespace
110                     api.createProfile(profile)
111                     api.uploadProfileContent(profile, profileFilePath)
112
113                     log.info("K8s Profile Upload Completed")
114                 }
115             }
116         }
117     }
118
119     fun prepareProfileFile(k8sRbProfileName: String): Path {
120         val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
121         val bluePrintBasePath: String = bluePrintContext.rootPath
122         var profileFilePath: Path = Paths.get(bluePrintBasePath.plus(File.separator).plus("Templates").plus(File.separator).plus("k8s-profiles").plus(File.separator).plus("${k8sRbProfileName}.tar.gz"))
123         log.info("Reading K8s profile file: ${profileFilePath}")
124
125         val profileFile = profileFilePath.toFile()
126
127         if (!profileFile.exists())
128             throw BluePrintProcessorException("K8s Profile template file ${profileFilePath} does not exists")
129
130         return profileFilePath
131     }
132
133     fun getTemplatePrefixList(executionRequest: ExecutionServiceInput): ArrayList<String> {
134         val result = ArrayList<String>()
135         for (prefix in executionRequest.payload.get("resource-assignment-request").get("template-prefix").elements())
136             result.add(prefix.asText())
137         return result
138     }
139
140     fun getResolvedParameter(payload: ObjectNode, keyName: String): String {
141         for (node in payload.get("resource-accumulator-resolved-data").elements()) {
142             if (node.get("param-name").asText().equals(keyName)) {
143                 return node.get("param-value").asText()
144             }
145         }
146         return ""
147     }
148
149     override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
150         log.info("Executing Recovery")
151         bluePrintRuntimeService.getBluePrintError().addError("${runtimeException.message}")
152     }
153
154     inner class K8sApi(val username: String, val password: String, val baseUrl: String, val definition: String,
155                        val definitionVersion: String) {
156         private val service: UploadFileRestClientService //BasicAuthRestClientService
157
158         init {
159             var mapOfHeaders = hashMapOf<String, String>()
160             mapOfHeaders.put("Accept", "application/json")
161             mapOfHeaders.put("Content-Type", "application/json")
162             mapOfHeaders.put("cache-control", " no-cache")
163             mapOfHeaders.put("Accept", "application/json")
164             var basicAuthRestClientProperties: BasicAuthRestClientProperties = BasicAuthRestClientProperties()
165             basicAuthRestClientProperties.username = username
166             basicAuthRestClientProperties.password = password
167             basicAuthRestClientProperties.url = "$baseUrl/api/multicloud-k8s/v1/v1/rb/definition/${definition}/${definitionVersion}"
168             basicAuthRestClientProperties.additionalHeaders = mapOfHeaders
169
170             this.service = UploadFileRestClientService(basicAuthRestClientProperties)
171         }
172
173         fun hasDefinition(): Boolean {
174             try {
175                 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name, "", "")
176                 print(result)
177                 if (result.status >= 200 && result.status < 300)
178                     return true
179                 else
180                     return false
181             } catch (e: Exception) {
182                 log.info("Caught exception trying to get k8s rb definition")
183                 throw BluePrintProcessorException("${e.message}")
184             }
185         }
186
187         fun hasProfile(profileName: String): Boolean {
188             try {
189                 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.GET.name,
190                         "/profile/$profileName", "")
191                 if (result.status >= 200 && result.status < 300)
192                     return true
193                 else {
194                     print(result)
195                     return false
196                 }
197             } catch (e: Exception) {
198                 log.info("Caught exception trying to get k8s rb profile")
199                 throw BluePrintProcessorException("${e.message}")
200             }
201         }
202
203         fun createProfile(profile: K8sProfile) {
204             val objectMapper = ObjectMapper()
205             val profileJsonString: String = objectMapper.writeValueAsString(profile)
206             try {
207                 val result: BlueprintWebClientService.WebClientResponse<String> = service.exchangeResource(HttpMethod.POST.name,
208                         "/profile", profileJsonString)
209                 if (result.status < 200 || result.status >= 300) {
210                     throw Exception(result.body)
211                 }
212             } catch (e: Exception) {
213                 log.info("Caught exception trying to create k8s rb profile ${profile.profileName}")
214                 throw BluePrintProcessorException("${e.message}")
215             }
216         }
217
218         fun uploadProfileContent(profile: K8sProfile, filePath: Path) {
219             try {
220                 val result: BlueprintWebClientService.WebClientResponse<String> = service.uploadBinaryFile(
221                         "/profile/${profile.profileName}/content", filePath)
222                 if (result.status < 200 || result.status >= 300) {
223                     throw Exception(result.body)
224                 }
225             } catch (e: Exception) {
226                 log.info("Caught exception trying to upload k8s rb profile ${profile.profileName}")
227                 throw BluePrintProcessorException("${e.message}")
228             }
229         }
230     }
231 }
232
233 class UploadFileRestClientService(
234         private val restClientProperties:
235         BasicAuthRestClientProperties
236 ) : BlueprintWebClientService {
237
238     override fun defaultHeaders(): Map<String, String> {
239
240         val encodedCredentials = setBasicAuth(
241                 restClientProperties.username,
242                 restClientProperties.password
243         )
244         return mapOf(
245                 HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
246                 HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,
247                 HttpHeaders.AUTHORIZATION to "Basic $encodedCredentials"
248         )
249     }
250
251     override fun host(uri: String): String {
252         return restClientProperties.url + uri
253     }
254
255     override fun convertToBasicHeaders(headers: Map<String, String>):
256             Array<BasicHeader> {
257         val customHeaders: MutableMap<String, String> = headers.toMutableMap()
258         // inject additionalHeaders
259         customHeaders.putAll(verifyAdditionalHeaders(restClientProperties))
260
261         if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
262             val encodedCredentials = setBasicAuth(
263                     restClientProperties.username,
264                     restClientProperties.password
265             )
266             customHeaders[HttpHeaders.AUTHORIZATION] =
267                     "Basic $encodedCredentials"
268         }
269         return super.convertToBasicHeaders(customHeaders)
270     }
271
272     private fun setBasicAuth(username: String, password: String): String {
273         val credentialsString = "$username:$password"
274         return Base64.getEncoder().encodeToString(
275                 credentialsString.toByteArray(Charset.defaultCharset())
276         )
277     }
278
279     @Throws(IOException::class, ClientProtocolException::class)
280     private fun performHttpCall(httpUriRequest: HttpUriRequest): BlueprintWebClientService.WebClientResponse<String> {
281         val httpResponse = httpClient().execute(httpUriRequest)
282         val statusCode = httpResponse.statusLine.statusCode
283         httpResponse.entity.content.use {
284             val body = IOUtils.toString(it, Charset.defaultCharset())
285             return BlueprintWebClientService.WebClientResponse(statusCode, body)
286         }
287     }
288
289     fun uploadBinaryFile(path: String, filePath: Path): BlueprintWebClientService.WebClientResponse<String> {
290         val convertedHeaders: Array<BasicHeader> = convertToBasicHeaders(defaultHeaders())
291         val httpPost = HttpPost(host(path))
292         val entity = EntityBuilder.create().setBinary(Files.readAllBytes(filePath)).build()
293         httpPost.setEntity(entity)
294         RestLoggerService.httpInvoking(convertedHeaders)
295         httpPost.setHeaders(convertedHeaders)
296         return performHttpCall(httpPost)
297     }
298 }
299
300 class K8sProfile {
301     @get:JsonProperty("rb-name")
302     var rbName: String? = null
303     @get:JsonProperty("rb-version")
304     var rbVersion: String? = null
305     @get:JsonProperty("profile-name")
306     var profileName: String? = null
307     @get:JsonProperty("namespace")
308     var namespace: String? = "default"
309
310     override fun toString(): String {
311         return "$rbName:$rbVersion:$profileName"
312     }
313
314     override fun equals(other: Any?): Boolean {
315         if (this === other) return true
316         if (javaClass != other?.javaClass) return false
317         return true
318     }
319
320     override fun hashCode(): Int {
321         return javaClass.hashCode()
322     }
323 }