Controller Blueprints MS
[ccsdk/cds.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     val seperator: String = "/"\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(seperator), 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(seperator), 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         Preconditions.checkArgument(StringUtils.isNotBlank(metaDataMap[BluePrintConstants.METADATA_TEMPLATE_NAME]), "failed to get template name metadata")\r
83         Preconditions.checkArgument(StringUtils.isNotBlank(metaDataMap[BluePrintConstants.METADATA_TEMPLATE_VERSION]), "failed to get template version metadata")\r
84         Preconditions.checkArgument(StringUtils.isNotBlank(metaDataMap[BluePrintConstants.METADATA_TEMPLATE_TAGS]), "failed to get template tags metadata")\r
85         Preconditions.checkArgument(StringUtils.isNotBlank(metaDataMap[BluePrintConstants.METADATA_TEMPLATE_AUTHOR]), "failed to get template author metadata")\r
86         Preconditions.checkArgument(StringUtils.isNotBlank(metaDataMap[BluePrintConstants.METADATA_USER_GROUPS]), "failed to get user groups metadata")\r
87         paths.removeAt(paths.lastIndex)\r
88     }\r
89 \r
90     @Throws(BluePrintException::class)\r
91     open fun validateArtifactTypes(artifactTypes: MutableMap<String, ArtifactType>) {\r
92         paths.add("artifact_types")\r
93         artifactTypes.forEach { artifactName, artifactType ->\r
94             paths.add(artifactName)\r
95             message.appendln("--> Artifact Type :" + paths.joinToString(seperator))\r
96             artifactType.properties?.let { validatePropertyDefinitions(artifactType.properties!!) }\r
97             paths.removeAt(paths.lastIndex)\r
98         }\r
99         paths.removeAt(paths.lastIndex)\r
100     }\r
101 \r
102     @Throws(BluePrintException::class)\r
103     open fun validateDataTypes(dataTypes: MutableMap<String, DataType>) {\r
104         paths.add("dataTypes")\r
105         dataTypes.forEach { dataTypeName, dataType ->\r
106             paths.add(dataTypeName)\r
107             message.appendln("--> Data Type :" + paths.joinToString(seperator))\r
108             dataType.properties?.let { validatePropertyDefinitions(dataType.properties!!) }\r
109             paths.removeAt(paths.lastIndex)\r
110         }\r
111         paths.removeAt(paths.lastIndex)\r
112     }\r
113 \r
114     @Throws(BluePrintException::class)\r
115     open fun validateNodeTypes(nodeTypes: MutableMap<String, NodeType>) {\r
116         paths.add("nodeTypes")\r
117         nodeTypes.forEach { nodeTypeName, nodeType ->\r
118             // Validate Single Node Type\r
119             validateNodeType(nodeTypeName,nodeType)\r
120         }\r
121         paths.removeAt(paths.lastIndex)\r
122     }\r
123 \r
124     @Throws(BluePrintException::class)\r
125     open fun validateNodeType(nodeTypeName: String, nodeType: NodeType) {\r
126         paths.add(nodeTypeName)\r
127         message.appendln("--> Node Type :" + paths.joinToString(seperator))\r
128         val derivedFrom: String = nodeType.derivedFrom\r
129         //Check Derived From\r
130         checkValidNodeTypesDerivedFrom(derivedFrom)\r
131 \r
132         nodeType.properties?.let { validatePropertyDefinitions(nodeType.properties!!) }\r
133         nodeType.interfaces?.let { validateInterfaceDefinitions(nodeType.interfaces!!) }\r
134         paths.removeAt(paths.lastIndex)\r
135     }\r
136 \r
137     @Throws(BluePrintException::class)\r
138     open fun validateTopologyTemplate(topologyTemplate: TopologyTemplate) {\r
139         paths.add("topology")\r
140         message.appendln("--> Topology Template")\r
141         topologyTemplate.inputs?.let { validateInputs(topologyTemplate.inputs!!) }\r
142         topologyTemplate.nodeTemplates?.let { validateNodeTemplates(topologyTemplate.nodeTemplates!!) }\r
143         topologyTemplate.workflows?.let { validateWorkFlows(topologyTemplate.workflows!!) }\r
144         paths.removeAt(paths.lastIndex)\r
145     }\r
146 \r
147     @Throws(BluePrintException::class)\r
148     open fun validateInputs(inputs: MutableMap<String, PropertyDefinition>) {\r
149         paths.add("inputs")\r
150         message.appendln("---> Input :" + paths.joinToString(seperator))\r
151         validatePropertyDefinitions(inputs)\r
152         paths.removeAt(paths.lastIndex)\r
153     }\r
154 \r
155     @Throws(BluePrintException::class)\r
156     open fun validateNodeTemplates(nodeTemplates: MutableMap<String, NodeTemplate>) {\r
157         paths.add("nodeTemplates")\r
158         nodeTemplates.forEach { nodeTemplateName, nodeTemplate ->\r
159             validateNodeTemplate(nodeTemplateName, nodeTemplate)\r
160         }\r
161         paths.removeAt(paths.lastIndex)\r
162     }\r
163 \r
164     @Throws(BluePrintException::class)\r
165     open fun validateNodeTemplate(nodeTemplateName : String, nodeTemplate: NodeTemplate) {\r
166         paths.add(nodeTemplateName)\r
167         message.appendln("---> Node Template :" + paths.joinToString(seperator))\r
168         val type: String = nodeTemplate.type\r
169 \r
170         val nodeType: NodeType = serviceTemplate.nodeTypes?.get(type)\r
171                 ?: throw BluePrintException(format("Failed to get node type definition  for node template : {}", nodeTemplateName))\r
172 \r
173         nodeTemplate.artifacts?.let { validateArtifactDefinitions(nodeTemplate.artifacts!!) }\r
174         nodeTemplate.properties?.let { validatePropertyAssignments(nodeType.properties!!, nodeTemplate.properties!!) }\r
175         nodeTemplate.capabilities?.let { validateCapabilityAssignments(nodeTemplate.capabilities!!) }\r
176         nodeTemplate.requirements?.let { validateRequirementAssignments(nodeTemplate.requirements!!) }\r
177         nodeTemplate.interfaces?.let { validateInterfaceAssignments(nodeTemplate.interfaces!!) }\r
178         paths.removeAt(paths.lastIndex)\r
179     }\r
180 \r
181     @Throws(BluePrintException::class)\r
182     open fun validateWorkFlows(workflows: MutableMap<String, Workflow>) {\r
183         paths.add("workflows")\r
184         workflows.forEach { workflowName, workflow ->\r
185 \r
186             // Validate Single workflow\r
187             validateWorkFlow(workflowName, workflow)\r
188         }\r
189         paths.removeAt(paths.lastIndex)\r
190     }\r
191 \r
192     @Throws(BluePrintException::class)\r
193     open fun validateWorkFlow(workflowName:String, workflow: Workflow) {\r
194         paths.add(workflowName)\r
195         message.appendln("---> Workflow :" + paths.joinToString(seperator))\r
196         // Step Validation Start\r
197         paths.add("steps")\r
198         workflow.steps?.forEach { stepName, step ->\r
199             paths.add(stepName)\r
200             message.appendln("----> Steps :" + paths.joinToString(seperator))\r
201             paths.removeAt(paths.lastIndex)\r
202         }\r
203         paths.removeAt(paths.lastIndex)\r
204         // Step Validation Ends\r
205         paths.removeAt(paths.lastIndex)\r
206     }\r
207 \r
208     @Throws(BluePrintException::class)\r
209     open fun validatePropertyDefinitions(properties: MutableMap<String, PropertyDefinition>) {\r
210         paths.add("properties")\r
211         properties.forEach { propertyName, propertyDefinition ->\r
212             paths.add(propertyName)\r
213             val dataType: String = propertyDefinition.type!!\r
214             if (BluePrintTypes.validPrimitiveTypes().contains(dataType)) {\r
215                 // Do Nothing\r
216             } else if (BluePrintTypes.validCollectionTypes().contains(dataType)) {\r
217                 val entrySchemaType: String = propertyDefinition.entrySchema?.type\r
218                         ?: throw BluePrintException(format("Entry schema for data type ({}) for the property ({}) not found", dataType, propertyName))\r
219                 checkPrimitiveOrComplex(entrySchemaType, propertyName)\r
220             } else {\r
221                 checkPropertyDataType(dataType, propertyName)\r
222             }\r
223             message.appendln("property " + paths.joinToString(seperator) + " of type " + dataType)\r
224             paths.removeAt(paths.lastIndex)\r
225         }\r
226         paths.removeAt(paths.lastIndex)\r
227     }\r
228 \r
229     @Throws(BluePrintException::class)\r
230     open fun validatePropertyAssignments(nodeTypeProperties: MutableMap<String, PropertyDefinition>,\r
231                                          properties: MutableMap<String, JsonNode>) {\r
232         properties.forEach { propertyName, propertyAssignment ->\r
233             val propertyDefinition: PropertyDefinition = nodeTypeProperties[propertyName]\r
234                     ?: throw BluePrintException(format("failed to get definition for the property ({})", propertyName))\r
235             // Check and Validate if Expression Node\r
236             val expressionData = BluePrintExpressionService.getExpressionData(propertyAssignment)\r
237             if (!expressionData.isExpression) {\r
238                 checkPropertyValue(propertyDefinition, propertyAssignment)\r
239             }\r
240         }\r
241     }\r
242 \r
243     @Throws(BluePrintException::class)\r
244     open fun validateArtifactDefinitions(artifacts: MutableMap<String, ArtifactDefinition>) {\r
245         paths.add("artifacts")\r
246         artifacts.forEach { artifactName, artifactDefinition ->\r
247             paths.add(artifactName)\r
248             message.appendln("Validating artifact " + paths.joinToString(seperator))\r
249             val type: String = artifactDefinition.type\r
250                     ?: throw BluePrintException("type is missing for artifact definition :" + artifactName)\r
251             // Check Artifact Type\r
252             checkValidArtifactType(type)\r
253 \r
254             val file: String = artifactDefinition.file\r
255                     ?: throw BluePrintException(format("file is missing for artifact definition : {}", artifactName))\r
256 \r
257             paths.removeAt(paths.lastIndex)\r
258         }\r
259         paths.removeAt(paths.lastIndex)\r
260     }\r
261 \r
262     @Throws(BluePrintException::class)\r
263     open fun validateCapabilityAssignments(capabilities: MutableMap<String, CapabilityAssignment>) {\r
264 \r
265     }\r
266 \r
267     @Throws(BluePrintException::class)\r
268     open fun validateRequirementAssignments(requirements: MutableMap<String, RequirementAssignment>) {\r
269 \r
270     }\r
271 \r
272     @Throws(BluePrintException::class)\r
273     open fun validateInterfaceAssignments(interfaces: MutableMap<String, InterfaceAssignment>) {\r
274 \r
275     }\r
276 \r
277     @Throws(BluePrintException::class)\r
278     open fun validateInterfaceDefinitions(interfaces: MutableMap<String, InterfaceDefinition>) {\r
279         paths.add("interfaces")\r
280         interfaces.forEach { interfaceName, interfaceDefinition ->\r
281             paths.add(interfaceName)\r
282             message.appendln("Validating : " + paths.joinToString(seperator))\r
283             interfaceDefinition.operations?.let { validateOperationDefinitions(interfaceDefinition.operations!!) }\r
284             paths.removeAt(paths.lastIndex)\r
285         }\r
286         paths.removeAt(paths.lastIndex)\r
287     }\r
288 \r
289     @Throws(BluePrintException::class)\r
290     open fun validateOperationDefinitions(operations: MutableMap<String, OperationDefinition>) {\r
291         paths.add("operations")\r
292         operations.forEach { opertaionName, operationDefinition ->\r
293             paths.add(opertaionName)\r
294             message.appendln("Validating : " + paths.joinToString(seperator))\r
295             operationDefinition.implementation?.let { validateImplementation(operationDefinition.implementation!!) }\r
296             operationDefinition.inputs?.let { validatePropertyDefinitions(operationDefinition.inputs!!) }\r
297             operationDefinition.outputs?.let { validatePropertyDefinitions(operationDefinition.outputs!!) }\r
298             paths.removeAt(paths.lastIndex)\r
299         }\r
300         paths.removeAt(paths.lastIndex)\r
301     }\r
302 \r
303     @Throws(BluePrintException::class)\r
304     open fun validateImplementation(implementation: Implementation) {\r
305         checkNotEmptyNThrow(implementation.primary)\r
306     }\r
307 \r
308     @Throws(BluePrintException::class)\r
309     open fun checkValidNodeType(nodeType : String) {\r
310 \r
311     }\r
312 \r
313     @Throws(BluePrintException::class)\r
314     open fun checkValidArtifactType(artifactType: String) {\r
315 \r
316         serviceTemplate.artifactTypes?.containsKey(artifactType)\r
317                 ?: throw BluePrintException(format("Failed to node type definition for artifact definition : {}", artifactType))\r
318     }\r
319 \r
320     @Throws(BluePrintException::class)\r
321     open fun checkValidNodeTypesDerivedFrom(derivedFrom: String) {\r
322 \r
323     }\r
324 \r
325     private fun checkPropertyValue(propertyDefinition: PropertyDefinition, jsonNode: JsonNode) {\r
326         //logger.info("validating path ({}), Property {}, value ({})", paths, propertyDefinition, jsonNode)\r
327     }\r
328 \r
329     private fun checkPropertyDataType(dataType: String, propertyName: String): Boolean {\r
330         if (checkDataType(dataType)) {\r
331             return true\r
332         } else {\r
333             throw BluePrintException(format("Data type ({}) for the property ({}) not found", dataType, propertyName))\r
334         }\r
335     }\r
336 \r
337     private fun checkPrimitiveOrComplex(dataType: String, propertyName: String): Boolean {\r
338         if (BluePrintTypes.validPrimitiveTypes().contains(dataType) || checkDataType(dataType)) {\r
339             return true\r
340         } else {\r
341             throw BluePrintException(format("Data type ({}) for the property ({}) is not valid", dataType))\r
342         }\r
343     }\r
344 \r
345     private fun checkDataType(key: String): Boolean {\r
346         return serviceTemplate.dataTypes?.containsKey(key) ?: false\r
347     }\r
348 \r
349     private fun checkNodeType(key: String): Boolean {\r
350         return serviceTemplate.nodeTypes?.containsKey(key) ?: false\r
351     }\r
352 \r
353 }