1270298a8feb6198b34977cfc0ff932d5650c250
[ccsdk/cds.git] /
1 /*
2  *  Copyright © 2017-2018 AT&T Intellectual Property.
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.resource.resolution
19
20 import kotlinx.coroutines.async
21 import kotlinx.coroutines.awaitAll
22 import kotlinx.coroutines.coroutineScope
23 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.ResourceResolutionDBService
24 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.db.TemplateResolutionService
25 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.processor.ResourceAssignmentProcessor
26 import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils
27 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
28 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
29 import org.onap.ccsdk.cds.controllerblueprints.core.asJsonType
30 import org.onap.ccsdk.cds.controllerblueprints.core.checkNotEmpty
31 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
32 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintTemplateService
33 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
34 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
35 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceDefinition
36 import org.onap.ccsdk.cds.controllerblueprints.resource.dict.utils.BulkResourceSequencingUtils
37 import org.slf4j.LoggerFactory
38 import org.springframework.context.ApplicationContext
39 import org.springframework.stereotype.Service
40
41 interface ResourceResolutionService {
42
43     fun registeredResourceSources(): List<String>
44
45     suspend fun resolveFromDatabase(bluePrintRuntimeService: BluePrintRuntimeService<*>, artifactTemplate: String,
46                                     resolutionKey: String): String
47
48     suspend fun resolveResources(bluePrintRuntimeService: BluePrintRuntimeService<*>, nodeTemplateName: String,
49                                  artifactNames: List<String>, properties: Map<String, Any>): MutableMap<String, String>
50
51     suspend fun resolveResources(bluePrintRuntimeService: BluePrintRuntimeService<*>, nodeTemplateName: String,
52                                  artifactPrefix: String, properties: Map<String, Any>): String
53
54     suspend fun resolveResourceAssignments(blueprintRuntimeService: BluePrintRuntimeService<*>,
55                                            resourceDefinitions: MutableMap<String, ResourceDefinition>,
56                                            resourceAssignments: MutableList<ResourceAssignment>,
57                                            artifactPrefix: String,
58                                            properties: Map<String, Any>)
59 }
60
61 @Service(ResourceResolutionConstants.SERVICE_RESOURCE_RESOLUTION)
62 open class ResourceResolutionServiceImpl(private var applicationContext: ApplicationContext,
63                                          private var resolutionResultService: TemplateResolutionService,
64                                          private var blueprintTemplateService: BluePrintTemplateService,
65                                          private var resourceResolutionDBService: ResourceResolutionDBService) :
66     ResourceResolutionService {
67
68     private val log = LoggerFactory.getLogger(ResourceResolutionService::class.java)
69
70     override fun registeredResourceSources(): List<String> {
71         return applicationContext.getBeanNamesForType(ResourceAssignmentProcessor::class.java)
72             .filter { it.startsWith(ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR) }
73             .map { it.substringAfter(ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR) }
74     }
75
76     override suspend fun resolveFromDatabase(bluePrintRuntimeService: BluePrintRuntimeService<*>,
77                                              artifactTemplate: String,
78                                              resolutionKey: String): String {
79         return resolutionResultService.read(bluePrintRuntimeService, artifactTemplate, resolutionKey)
80     }
81
82     override suspend fun resolveResources(bluePrintRuntimeService: BluePrintRuntimeService<*>, nodeTemplateName: String,
83                                           artifactNames: List<String>,
84                                           properties: Map<String, Any>): MutableMap<String, String> {
85
86         val resolvedParams: MutableMap<String, String> = hashMapOf()
87         artifactNames.forEach { artifactName ->
88             val resolvedContent = resolveResources(bluePrintRuntimeService, nodeTemplateName,
89                 artifactName, properties)
90             resolvedParams[artifactName] = resolvedContent
91         }
92         return resolvedParams
93     }
94
95     override suspend fun resolveResources(bluePrintRuntimeService: BluePrintRuntimeService<*>, nodeTemplateName: String,
96                                           artifactPrefix: String, properties: Map<String, Any>): String {
97
98         // Velocity Artifact Definition Name
99         val artifactTemplate = "$artifactPrefix-template"
100         // Resource Assignment Artifact Definition Name
101         val artifactMapping = "$artifactPrefix-mapping"
102
103         val resolvedContent: String
104         log.info("Resolving resource for template artifact($artifactTemplate) with resource assignment artifact($artifactMapping)")
105
106         val resourceAssignmentContent =
107             bluePrintRuntimeService.resolveNodeTemplateArtifact(nodeTemplateName, artifactMapping)
108
109         val resourceAssignments: MutableList<ResourceAssignment> =
110             JacksonUtils.getListFromJson(resourceAssignmentContent, ResourceAssignment::class.java)
111                     as? MutableList<ResourceAssignment>
112                 ?: throw BluePrintProcessorException("couldn't get Dictionary Definitions")
113
114         // Get the Resource Dictionary Name
115         val resourceDefinitions: MutableMap<String, ResourceDefinition> = ResourceAssignmentUtils
116             .resourceDefinitions(bluePrintRuntimeService.bluePrintContext().rootPath)
117
118         // Resolve resources
119         resolveResourceAssignments(bluePrintRuntimeService,
120             resourceDefinitions,
121             resourceAssignments,
122             artifactPrefix,
123             properties)
124
125         val resolvedParamJsonContent =
126             ResourceAssignmentUtils.generateResourceDataForAssignments(resourceAssignments.toList())
127
128         resolvedContent = blueprintTemplateService.generateContent(bluePrintRuntimeService, nodeTemplateName,
129             artifactTemplate, resolvedParamJsonContent)
130
131         if (properties.containsKey(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT)
132             && properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] as Boolean) {
133             resolutionResultService.write(properties, resolvedContent, bluePrintRuntimeService, artifactPrefix)
134             log.info("template resolution saved into database successfully : ($properties)")
135         }
136
137         return resolvedContent
138     }
139
140     /**
141      * Iterate the Batch, get the Resource Assignment, dictionary Name, Look for the Resource definition for the
142      * name, then get the type of the Resource Definition, Get the instance for the Resource Type and process the
143      * request.
144      */
145     override suspend fun resolveResourceAssignments(blueprintRuntimeService: BluePrintRuntimeService<*>,
146                                                     resourceDefinitions: MutableMap<String, ResourceDefinition>,
147                                                     resourceAssignments: MutableList<ResourceAssignment>,
148                                                     artifactPrefix: String,
149                                                     properties: Map<String, Any>) {
150
151         val bulkSequenced = BulkResourceSequencingUtils.process(resourceAssignments)
152         val resourceAssignmentRuntimeService = blueprintRuntimeService as ResourceAssignmentRuntimeService
153
154         coroutineScope {
155             bulkSequenced.forEach { batchResourceAssignments ->
156                 // Execute Non Dependent Assignments in parallel ( ie asynchronously )
157                 val deferred = batchResourceAssignments.filter { it.name != "*" && it.name != "start" }
158                     .map { resourceAssignment ->
159                         async {
160                             val dictionaryName = resourceAssignment.dictionaryName
161                             val dictionarySource = resourceAssignment.dictionarySource
162
163                             val processorName = processorName(dictionaryName!!, dictionarySource!!, resourceDefinitions)
164
165                             val resourceAssignmentProcessor =
166                                 applicationContext.getBean(processorName) as? ResourceAssignmentProcessor
167                                     ?: throw BluePrintProcessorException("failed to get resource processor ($processorName) " +
168                                             "for resource assignment(${resourceAssignment.name})")
169                             try {
170                                 // Set BluePrint Runtime Service
171                                 resourceAssignmentProcessor.raRuntimeService = resourceAssignmentRuntimeService
172                                 // Set Resource Dictionaries
173                                 resourceAssignmentProcessor.resourceDictionaries = resourceDefinitions
174                                 // Invoke Apply Method
175                                 resourceAssignmentProcessor.applyNB(resourceAssignment)
176
177                                 if (isToStore(properties)) {
178                                     resourceResolutionDBService.write(properties,
179                                         blueprintRuntimeService,
180                                         artifactPrefix,
181                                         resourceAssignment)
182                                     log.info("Resource resolution saved into database successfully : ($resourceAssignment)")
183                                 }
184
185                                 // Set errors from RA
186                                 blueprintRuntimeService.setBluePrintError(resourceAssignmentRuntimeService.getBluePrintError())
187                             } catch (e: RuntimeException) {
188                                 log.error("Fail in processing ${resourceAssignment.name}", e)
189                                 throw BluePrintProcessorException(e)
190                             }
191                         }
192                     }
193                 log.debug("Resolving (${deferred.size})resources parallel.")
194                 deferred.awaitAll()
195             }
196         }
197
198     }
199
200     private fun isToStore(properties: Map<String, Any>): Boolean {
201         return properties.containsKey(ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT)
202                 && properties[ResourceResolutionConstants.RESOURCE_RESOLUTION_INPUT_STORE_RESULT] as Boolean
203     }
204
205
206     /**
207      * If the Source instance is "input", then it is not mandatory to have source Resource Definition, So it can
208      *  derive the default input processor.
209      */
210     private fun processorName(dictionaryName: String, dictionarySource: String,
211                               resourceDefinitions: MutableMap<String, ResourceDefinition>): String {
212         val processorName: String = when (dictionarySource) {
213             "input" -> {
214                 "${ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR}source-input"
215             }
216             "default" -> {
217                 "${ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR}source-default"
218             }
219             else -> {
220                 val resourceDefinition = resourceDefinitions[dictionaryName]
221                     ?: throw BluePrintProcessorException("couldn't get resource dictionary definition for $dictionaryName")
222
223                 val resourceSource = resourceDefinition.sources[dictionarySource]
224                     ?: throw BluePrintProcessorException("couldn't get resource definition $dictionaryName source($dictionarySource)")
225
226                 ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR.plus(resourceSource.type)
227             }
228         }
229         checkNotEmpty(processorName) {
230             "couldn't get processor name for resource dictionary definition($dictionaryName) source($dictionarySource)"
231         }
232
233         return processorName
234
235     }
236 }