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