Merge "Controller Blueprints Microservice"
[ccsdk/apps.git] / ms / controllerblueprints / modules / core / src / main / kotlin / org / onap / ccsdk / apps / controllerblueprints / core / service / BluePrintValidatorService.kt
1 /*\r
2  * Copyright © 2017-2018 AT&T Intellectual Property.\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.apps.controllerblueprints.core.service\r
18 \r
19 import com.fasterxml.jackson.databind.JsonNode\r
20 import com.google.common.base.Preconditions\r
21 import org.apache.commons.lang3.StringUtils\r
22 import org.onap.ccsdk.apps.controllerblueprints.core.*\r
23 import org.onap.ccsdk.apps.controllerblueprints.core.data.*\r
24 import org.slf4j.Logger\r
25 import org.slf4j.LoggerFactory\r
26 import java.io.Serializable\r
27 \r
28 /**\r
29  *\r
30  *\r
31  * @author Brinda Santh\r
32  */\r
33 interface BluePrintValidatorService : Serializable {\r
34 \r
35     @Throws(BluePrintException::class)\r
36     fun validateBlueprint(bluePrintContext: BluePrintContext, properties: MutableMap<String, Any>)\r
37 \r
38     @Throws(BluePrintException::class)\r
39     fun validateBlueprint(serviceTemplate: ServiceTemplate, properties: MutableMap<String, Any>)\r
40 }\r
41 \r
42 open class BluePrintValidatorDefaultService : BluePrintValidatorService {\r
43 \r
44     val logger: Logger = LoggerFactory.getLogger(BluePrintValidatorDefaultService::class.toString())\r
45 \r
46     lateinit var bluePrintContext: BluePrintContext\r
47     lateinit var serviceTemplate: ServiceTemplate\r
48     lateinit var properties: MutableMap<String, Any>\r
49     var message: StringBuilder = StringBuilder()\r
50     private val separator: String = BluePrintConstants.PATH_DIVIDER\r
51     var paths: MutableList<String> = arrayListOf()\r
52 \r
53     @Throws(BluePrintException::class)\r
54     override fun validateBlueprint(bluePrintContext: BluePrintContext, properties: MutableMap<String, Any>) {\r
55         validateBlueprint(bluePrintContext.serviceTemplate,properties)\r
56     }\r
57 \r
58     @Throws(BluePrintException::class)\r
59     override fun validateBlueprint(serviceTemplate: ServiceTemplate, properties: MutableMap<String, Any>) {\r
60         this.bluePrintContext = BluePrintContext(serviceTemplate)\r
61         this.serviceTemplate = serviceTemplate\r
62         this.properties = properties\r
63         try {\r
64             message.appendln("-> Config Blueprint")\r
65             serviceTemplate.metadata?.let { validateMetadata(serviceTemplate.metadata!!) }\r
66             serviceTemplate.artifactTypes?.let { validateArtifactTypes(serviceTemplate.artifactTypes!!) }\r
67             serviceTemplate.dataTypes?.let { validateDataTypes(serviceTemplate.dataTypes!!) }\r
68             serviceTemplate.nodeTypes?.let { validateNodeTypes(serviceTemplate.nodeTypes!!) }\r
69             serviceTemplate.topologyTemplate?.let { validateTopologyTemplate(serviceTemplate.topologyTemplate!!) }\r
70         } catch (e: Exception) {\r
71             logger.error("validation failed in the path : {}", paths.joinToString(separator), e)\r
72             logger.error("validation trace message :{} ", message)\r
73             throw BluePrintException(e,\r
74                     format("failed to validate blueprint on path ({}) with message {}"\r
75                             , paths.joinToString(separator), e.message))\r
76         }\r
77     }\r
78 \r
79     @Throws(BluePrintException::class)\r
80     open fun validateMetadata(metaDataMap: MutableMap<String, String>) {\r
81         paths.add("metadata")\r
82 \r
83         val templateName = metaDataMap[BluePrintConstants.METADATA_TEMPLATE_NAME]\r
84         val templateVersion = metaDataMap[BluePrintConstants.METADATA_TEMPLATE_VERSION]\r
85         val templateTags = metaDataMap[BluePrintConstants.METADATA_TEMPLATE_TAGS]\r
86         val templateAuthor = metaDataMap[BluePrintConstants.METADATA_TEMPLATE_AUTHOR]\r
87 \r
88         Preconditions.checkArgument(StringUtils.isNotBlank(templateName), "failed to get template name metadata")\r
89         Preconditions.checkArgument(StringUtils.isNotBlank(templateVersion), "failed to get template version metadata")\r
90         Preconditions.checkArgument(StringUtils.isNotBlank(templateTags), "failed to get template tags metadata")\r
91         Preconditions.checkArgument(StringUtils.isNotBlank(templateAuthor), "failed to get template author metadata")\r
92         paths.removeAt(paths.lastIndex)\r
93     }\r
94 \r
95     @Throws(BluePrintException::class)\r
96     open fun validateArtifactTypes(artifactTypes: MutableMap<String, ArtifactType>) {\r
97         paths.add("artifact_types")\r
98         artifactTypes.forEach { artifactName, artifactType ->\r
99             paths.add(artifactName)\r
100             message.appendln("--> Artifact Type :" + paths.joinToString(separator))\r
101             artifactType.properties?.let { validatePropertyDefinitions(artifactType.properties!!) }\r
102             paths.removeAt(paths.lastIndex)\r
103         }\r
104         paths.removeAt(paths.lastIndex)\r
105     }\r
106 \r
107     @Throws(BluePrintException::class)\r
108     open fun validateDataTypes(dataTypes: MutableMap<String, DataType>) {\r
109         paths.add("dataTypes")\r
110         dataTypes.forEach { dataTypeName, dataType ->\r
111             paths.add(dataTypeName)\r
112             message.appendln("--> Data Type :" + paths.joinToString(separator))\r
113             dataType.properties?.let { validatePropertyDefinitions(dataType.properties!!) }\r
114             paths.removeAt(paths.lastIndex)\r
115         }\r
116         paths.removeAt(paths.lastIndex)\r
117     }\r
118 \r
119     @Throws(BluePrintException::class)\r
120     open fun validateNodeTypes(nodeTypes: MutableMap<String, NodeType>) {\r
121         paths.add("nodeTypes")\r
122         nodeTypes.forEach { nodeTypeName, nodeType ->\r
123             // Validate Single Node Type\r
124             validateNodeType(nodeTypeName,nodeType)\r
125         }\r
126         paths.removeAt(paths.lastIndex)\r
127     }\r
128 \r
129     @Throws(BluePrintException::class)\r
130     open fun validateNodeType(nodeTypeName: String, nodeType: NodeType) {\r
131         paths.add(nodeTypeName)\r
132         message.appendln("--> Node Type :" + paths.joinToString(separator))\r
133         val derivedFrom: String = nodeType.derivedFrom\r
134         //Check Derived From\r
135         checkValidNodeTypesDerivedFrom(derivedFrom)\r
136 \r
137         nodeType.properties?.let { validatePropertyDefinitions(nodeType.properties!!) }\r
138         nodeType.interfaces?.let { validateInterfaceDefinitions(nodeType.interfaces!!) }\r
139         paths.removeAt(paths.lastIndex)\r
140     }\r
141 \r
142     @Throws(BluePrintException::class)\r
143     open fun validateTopologyTemplate(topologyTemplate: TopologyTemplate) {\r
144         paths.add("topology")\r
145         message.appendln("--> Topology Template")\r
146         topologyTemplate.inputs?.let { validateInputs(topologyTemplate.inputs!!) }\r
147         topologyTemplate.nodeTemplates?.let { validateNodeTemplates(topologyTemplate.nodeTemplates!!) }\r
148         topologyTemplate.workflows?.let { validateWorkFlows(topologyTemplate.workflows!!) }\r
149         paths.removeAt(paths.lastIndex)\r
150     }\r
151 \r
152     @Throws(BluePrintException::class)\r
153     open fun validateInputs(inputs: MutableMap<String, PropertyDefinition>) {\r
154         paths.add("inputs")\r
155         message.appendln("---> Input :" + paths.joinToString(separator))\r
156         validatePropertyDefinitions(inputs)\r
157         paths.removeAt(paths.lastIndex)\r
158     }\r
159 \r
160     @Throws(BluePrintException::class)\r
161     open fun validateNodeTemplates(nodeTemplates: MutableMap<String, NodeTemplate>) {\r
162         paths.add("nodeTemplates")\r
163         nodeTemplates.forEach { nodeTemplateName, nodeTemplate ->\r
164             validateNodeTemplate(nodeTemplateName, nodeTemplate)\r
165         }\r
166         paths.removeAt(paths.lastIndex)\r
167     }\r
168 \r
169     @Throws(BluePrintException::class)\r
170     open fun validateNodeTemplate(nodeTemplateName : String, nodeTemplate: NodeTemplate) {\r
171         paths.add(nodeTemplateName)\r
172         message.appendln("---> Node Template :" + paths.joinToString(separator))\r
173         val type: String = nodeTemplate.type\r
174 \r
175         val nodeType: NodeType = serviceTemplate.nodeTypes?.get(type)\r
176                 ?: throw BluePrintException(format("Failed to get node type definition  for node template : {}", nodeTemplateName))\r
177 \r
178         nodeTemplate.artifacts?.let { validateArtifactDefinitions(nodeTemplate.artifacts!!) }\r
179         nodeTemplate.properties?.let { validatePropertyAssignments(nodeType.properties!!, nodeTemplate.properties!!) }\r
180         nodeTemplate.capabilities?.let { validateCapabilityAssignments(nodeTemplate.capabilities!!) }\r
181         nodeTemplate.requirements?.let { validateRequirementAssignments(nodeTemplate.requirements!!) }\r
182         nodeTemplate.interfaces?.let { validateInterfaceAssignments(nodeTemplate.interfaces!!) }\r
183         paths.removeAt(paths.lastIndex)\r
184     }\r
185 \r
186     @Throws(BluePrintException::class)\r
187     open fun validateWorkFlows(workflows: MutableMap<String, Workflow>) {\r
188         paths.add("workflows")\r
189         workflows.forEach { workflowName, workflow ->\r
190 \r
191             // Validate Single workflow\r
192             validateWorkFlow(workflowName, workflow)\r
193         }\r
194         paths.removeAt(paths.lastIndex)\r
195     }\r
196 \r
197     @Throws(BluePrintException::class)\r
198     open fun validateWorkFlow(workflowName:String, workflow: Workflow) {\r
199         paths.add(workflowName)\r
200         message.appendln("---> Workflow :" + paths.joinToString(separator))\r
201         // Step Validation Start\r
202         paths.add("steps")\r
203         workflow.steps?.forEach { stepName, step ->\r
204             paths.add(stepName)\r
205             message.appendln("----> Steps :" + paths.joinToString(separator))\r
206             paths.removeAt(paths.lastIndex)\r
207         }\r
208         paths.removeAt(paths.lastIndex)\r
209         // Step Validation Ends\r
210         paths.removeAt(paths.lastIndex)\r
211     }\r
212 \r
213     @Throws(BluePrintException::class)\r
214     open fun validatePropertyDefinitions(properties: MutableMap<String, PropertyDefinition>) {\r
215         paths.add("properties")\r
216         properties.forEach { propertyName, propertyDefinition ->\r
217             paths.add(propertyName)\r
218             val dataType: String = propertyDefinition.type!!\r
219             if (BluePrintTypes.validPrimitiveTypes().contains(dataType)) {\r
220                 // Do Nothing\r
221             } else if (BluePrintTypes.validCollectionTypes().contains(dataType)) {\r
222                 val entrySchemaType: String = propertyDefinition.entrySchema?.type\r
223                         ?: throw BluePrintException(format("Entry schema for data type ({}) for the property ({}) not found", dataType, propertyName))\r
224                 checkPrimitiveOrComplex(entrySchemaType, propertyName)\r
225             } else {\r
226                 checkPropertyDataType(dataType, propertyName)\r
227             }\r
228             message.appendln("property " + paths.joinToString(separator) + " of type " + dataType)\r
229             paths.removeAt(paths.lastIndex)\r
230         }\r
231         paths.removeAt(paths.lastIndex)\r
232     }\r
233 \r
234     @Throws(BluePrintException::class)\r
235     open fun validatePropertyAssignments(nodeTypeProperties: MutableMap<String, PropertyDefinition>,\r
236                                          properties: MutableMap<String, JsonNode>) {\r
237         properties.forEach { propertyName, propertyAssignment ->\r
238             val propertyDefinition: PropertyDefinition = nodeTypeProperties[propertyName]\r
239                     ?: throw BluePrintException(format("failed to get definition for the property ({})", propertyName))\r
240             // Check and Validate if Expression Node\r
241             val expressionData = BluePrintExpressionService.getExpressionData(propertyAssignment)\r
242             if (!expressionData.isExpression) {\r
243                 checkPropertyValue(propertyDefinition, propertyAssignment)\r
244             }\r
245         }\r
246     }\r
247 \r
248     @Throws(BluePrintException::class)\r
249     open fun validateArtifactDefinitions(artifacts: MutableMap<String, ArtifactDefinition>) {\r
250         paths.add("artifacts")\r
251         artifacts.forEach { artifactName, artifactDefinition ->\r
252             paths.add(artifactName)\r
253             message.appendln("Validating artifact " + paths.joinToString(separator))\r
254             val type: String = artifactDefinition.type\r
255                     ?: throw BluePrintException("type is missing for artifact definition :" + artifactName)\r
256             // Check Artifact Type\r
257             checkValidArtifactType(type)\r
258 \r
259             val file: String = artifactDefinition.file\r
260                     ?: throw BluePrintException(format("file is missing for artifact definition : {}", artifactName))\r
261 \r
262             paths.removeAt(paths.lastIndex)\r
263         }\r
264         paths.removeAt(paths.lastIndex)\r
265     }\r
266 \r
267     @Throws(BluePrintException::class)\r
268     open fun validateCapabilityAssignments(capabilities: MutableMap<String, CapabilityAssignment>) {\r
269 \r
270     }\r
271 \r
272     @Throws(BluePrintException::class)\r
273     open fun validateRequirementAssignments(requirements: MutableMap<String, RequirementAssignment>) {\r
274 \r
275     }\r
276 \r
277     @Throws(BluePrintException::class)\r
278     open fun validateInterfaceAssignments(interfaces: MutableMap<String, InterfaceAssignment>) {\r
279 \r
280     }\r
281 \r
282     @Throws(BluePrintException::class)\r
283     open fun validateInterfaceDefinitions(interfaces: MutableMap<String, InterfaceDefinition>) {\r
284         paths.add("interfaces")\r
285         interfaces.forEach { interfaceName, interfaceDefinition ->\r
286             paths.add(interfaceName)\r
287             message.appendln("Validating : " + paths.joinToString(separator))\r
288             interfaceDefinition.operations?.let { validateOperationDefinitions(interfaceDefinition.operations!!) }\r
289             paths.removeAt(paths.lastIndex)\r
290         }\r
291         paths.removeAt(paths.lastIndex)\r
292     }\r
293 \r
294     @Throws(BluePrintException::class)\r
295     open fun validateOperationDefinitions(operations: MutableMap<String, OperationDefinition>) {\r
296         paths.add("operations")\r
297         operations.forEach { opertaionName, operationDefinition ->\r
298             paths.add(opertaionName)\r
299             message.appendln("Validating : " + paths.joinToString(separator))\r
300             operationDefinition.implementation?.let { validateImplementation(operationDefinition.implementation!!) }\r
301             operationDefinition.inputs?.let { validatePropertyDefinitions(operationDefinition.inputs!!) }\r
302             operationDefinition.outputs?.let { validatePropertyDefinitions(operationDefinition.outputs!!) }\r
303             paths.removeAt(paths.lastIndex)\r
304         }\r
305         paths.removeAt(paths.lastIndex)\r
306     }\r
307 \r
308     @Throws(BluePrintException::class)\r
309     open fun validateImplementation(implementation: Implementation) {\r
310         checkNotEmptyNThrow(implementation.primary)\r
311     }\r
312 \r
313     @Throws(BluePrintException::class)\r
314     open fun checkValidNodeType(nodeType : String) {\r
315 \r
316     }\r
317 \r
318     @Throws(BluePrintException::class)\r
319     open fun checkValidArtifactType(artifactType: String) {\r
320 \r
321         serviceTemplate.artifactTypes?.containsKey(artifactType)\r
322                 ?: throw BluePrintException(format("Failed to node type definition for artifact definition : {}", artifactType))\r
323     }\r
324 \r
325     @Throws(BluePrintException::class)\r
326     open fun checkValidNodeTypesDerivedFrom(derivedFrom: String) {\r
327 \r
328     }\r
329 \r
330     private fun checkPropertyValue(propertyDefinition: PropertyDefinition, jsonNode: JsonNode) {\r
331         //logger.info("validating path ({}), Property {}, value ({})", paths, propertyDefinition, jsonNode)\r
332     }\r
333 \r
334     private fun checkPropertyDataType(dataType: String, propertyName: String): Boolean {\r
335         if (checkDataType(dataType)) {\r
336             return true\r
337         } else {\r
338             throw BluePrintException(format("Data type ({}) for the property ({}) not found", dataType, propertyName))\r
339         }\r
340     }\r
341 \r
342     private fun checkPrimitiveOrComplex(dataType: String, propertyName: String): Boolean {\r
343         if (BluePrintTypes.validPrimitiveTypes().contains(dataType) || checkDataType(dataType)) {\r
344             return true\r
345         } else {\r
346             throw BluePrintException(format("Data type ({}) for the property ({}) is not valid", dataType))\r
347         }\r
348     }\r
349 \r
350     private fun checkDataType(key: String): Boolean {\r
351         return serviceTemplate.dataTypes?.containsKey(key) ?: false\r
352     }\r
353 \r
354     private fun checkNodeType(key: String): Boolean {\r
355         return serviceTemplate.nodeTypes?.containsKey(key) ?: false\r
356     }\r
357 \r
358 }