a60f532e08ade7ea662506527d28c7e19f4c48bb
[ccsdk/cds.git] /
1 /*\r
2  * Copyright © 2017-2018 AT&T Intellectual Property.\r
3  * Modifications Copyright © 2018 IBM.\r
4  *\r
5  * Licensed under the Apache License, Version 2.0 (the "License");\r
6  * you may not use this file except in compliance with the License.\r
7  * You may obtain a copy of the License at\r
8  *\r
9  *     http://www.apache.org/licenses/LICENSE-2.0\r
10  *\r
11  * Unless required by applicable law or agreed to in writing, software\r
12  * distributed under the License is distributed on an "AS IS" BASIS,\r
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
14  * See the License for the specific language governing permissions and\r
15  * limitations under the License.\r
16  */\r
17 \r
18 package org.onap.ccsdk.apps.controllerblueprints.core.service\r
19 \r
20 import com.fasterxml.jackson.databind.JsonNode\r
21 import com.google.common.base.Preconditions\r
22 import org.apache.commons.lang3.StringUtils\r
23 import org.onap.ccsdk.apps.controllerblueprints.core.*\r
24 import org.onap.ccsdk.apps.controllerblueprints.core.data.*\r
25 import com.att.eelf.configuration.EELFLogger\r
26 import com.att.eelf.configuration.EELFManager\r
27 import org.onap.ccsdk.apps.controllerblueprints.core.utils.JacksonUtils\r
28 import java.io.Serializable\r
29 \r
30 /**\r
31  *\r
32  *\r
33  * @author Brinda Santh\r
34  */\r
35 interface BluePrintValidatorService : Serializable {\r
36 \r
37     @Throws(BluePrintException::class)\r
38     fun validateBlueprint(bluePrintContext: BluePrintContext, properties: MutableMap<String, Any>)\r
39 \r
40     @Throws(BluePrintException::class)\r
41     fun validateBlueprint(serviceTemplate: ServiceTemplate, properties: MutableMap<String, Any>)\r
42 }\r
43 \r
44 open class BluePrintValidatorDefaultService : BluePrintValidatorService {\r
45 \r
46     val log: EELFLogger = EELFManager.getInstance().getLogger(BluePrintValidatorDefaultService::class.toString())\r
47 \r
48     lateinit var bluePrintContext: BluePrintContext\r
49     lateinit var serviceTemplate: ServiceTemplate\r
50     lateinit var properties: MutableMap<String, Any>\r
51     var message: StringBuilder = StringBuilder()\r
52     private val separator: String = BluePrintConstants.PATH_DIVIDER\r
53     var paths: MutableList<String> = arrayListOf()\r
54 \r
55     @Throws(BluePrintException::class)\r
56     override fun validateBlueprint(bluePrintContext: BluePrintContext, properties: MutableMap<String, Any>) {\r
57         validateBlueprint(bluePrintContext.serviceTemplate, properties)\r
58     }\r
59 \r
60     @Throws(BluePrintException::class)\r
61     override fun validateBlueprint(serviceTemplate: ServiceTemplate, properties: MutableMap<String, Any>) {\r
62         this.bluePrintContext = BluePrintContext(serviceTemplate)\r
63         this.serviceTemplate = serviceTemplate\r
64         this.properties = properties\r
65         try {\r
66             message.appendln("-> Config Blueprint")\r
67             serviceTemplate.metadata?.let { validateMetadata(serviceTemplate.metadata!!) }\r
68             serviceTemplate.artifactTypes?.let { validateArtifactTypes(serviceTemplate.artifactTypes!!) }\r
69             serviceTemplate.dataTypes?.let { validateDataTypes(serviceTemplate.dataTypes!!) }\r
70             serviceTemplate.nodeTypes?.let { validateNodeTypes(serviceTemplate.nodeTypes!!) }\r
71             serviceTemplate.topologyTemplate?.let { validateTopologyTemplate(serviceTemplate.topologyTemplate!!) }\r
72         } catch (e: Exception) {\r
73             log.error("validation failed in the path : {}", paths.joinToString(separator), e)\r
74             log.error("validation trace message :{} ", message)\r
75             throw BluePrintException(e,\r
76                     format("failed to validate blueprint on path ({}) with message {}"\r
77                             , paths.joinToString(separator), e.message))\r
78         }\r
79     }\r
80 \r
81     @Throws(BluePrintException::class)\r
82     open fun validateMetadata(metaDataMap: MutableMap<String, String>) {\r
83         paths.add("metadata")\r
84 \r
85         val templateName = metaDataMap[BluePrintConstants.METADATA_TEMPLATE_NAME]\r
86         val templateVersion = metaDataMap[BluePrintConstants.METADATA_TEMPLATE_VERSION]\r
87         val templateTags = metaDataMap[BluePrintConstants.METADATA_TEMPLATE_TAGS]\r
88         val templateAuthor = metaDataMap[BluePrintConstants.METADATA_TEMPLATE_AUTHOR]\r
89 \r
90         Preconditions.checkArgument(StringUtils.isNotBlank(templateName), "failed to get template name metadata")\r
91         Preconditions.checkArgument(StringUtils.isNotBlank(templateVersion), "failed to get template version metadata")\r
92         Preconditions.checkArgument(StringUtils.isNotBlank(templateTags), "failed to get template tags metadata")\r
93         Preconditions.checkArgument(StringUtils.isNotBlank(templateAuthor), "failed to get template author metadata")\r
94         paths.removeAt(paths.lastIndex)\r
95     }\r
96 \r
97     @Throws(BluePrintException::class)\r
98     open fun validateArtifactTypes(artifactTypes: MutableMap<String, ArtifactType>) {\r
99         paths.add("artifact_types")\r
100         artifactTypes.forEach { artifactName, artifactType ->\r
101             paths.add(artifactName)\r
102             message.appendln("--> Artifact Type :" + paths.joinToString(separator))\r
103             artifactType.properties?.let { validatePropertyDefinitions(artifactType.properties!!) }\r
104             paths.removeAt(paths.lastIndex)\r
105         }\r
106         paths.removeAt(paths.lastIndex)\r
107     }\r
108 \r
109     @Throws(BluePrintException::class)\r
110     open fun validateDataTypes(dataTypes: MutableMap<String, DataType>) {\r
111         paths.add("dataTypes")\r
112         dataTypes.forEach { dataTypeName, dataType ->\r
113             paths.add(dataTypeName)\r
114             message.appendln("--> Data Type :" + paths.joinToString(separator))\r
115             dataType.properties?.let { validatePropertyDefinitions(dataType.properties!!) }\r
116             paths.removeAt(paths.lastIndex)\r
117         }\r
118         paths.removeAt(paths.lastIndex)\r
119     }\r
120 \r
121     @Throws(BluePrintException::class)\r
122     open fun validateNodeTypes(nodeTypes: MutableMap<String, NodeType>) {\r
123         paths.add("nodeTypes")\r
124         nodeTypes.forEach { nodeTypeName, nodeType ->\r
125             // Validate Single Node Type\r
126             validateNodeType(nodeTypeName, nodeType)\r
127         }\r
128         paths.removeAt(paths.lastIndex)\r
129     }\r
130 \r
131     @Throws(BluePrintException::class)\r
132     open fun validateNodeType(nodeTypeName: String, nodeType: NodeType) {\r
133         paths.add(nodeTypeName)\r
134         message.appendln("--> Node Type :" + paths.joinToString(separator))\r
135         val derivedFrom: String = nodeType.derivedFrom\r
136         //Check Derived From\r
137         checkValidNodeTypesDerivedFrom(nodeTypeName, derivedFrom)\r
138 \r
139         nodeType.properties?.let { validatePropertyDefinitions(nodeType.properties!!) }\r
140         nodeType.interfaces?.let { validateInterfaceDefinitions(nodeType.interfaces!!) }\r
141         paths.removeAt(paths.lastIndex)\r
142     }\r
143 \r
144     @Throws(BluePrintException::class)\r
145     open fun checkValidNodeTypesDerivedFrom(nodeTypeName: String, derivedFrom: String) {\r
146         check(BluePrintTypes.validNodeTypeDerivedFroms.contains(derivedFrom)) {\r
147             throw BluePrintException(format("Failed to get node type ({})'s  derived from({}) definition ", nodeTypeName, derivedFrom))\r
148         }\r
149     }\r
150 \r
151     @Throws(BluePrintException::class)\r
152     open fun validateTopologyTemplate(topologyTemplate: TopologyTemplate) {\r
153         paths.add("topology")\r
154         message.appendln("--> Topology Template")\r
155         topologyTemplate.inputs?.let { validateInputs(topologyTemplate.inputs!!) }\r
156         topologyTemplate.nodeTemplates?.let { validateNodeTemplates(topologyTemplate.nodeTemplates!!) }\r
157         topologyTemplate.workflows?.let { validateWorkFlows(topologyTemplate.workflows!!) }\r
158         paths.removeAt(paths.lastIndex)\r
159     }\r
160 \r
161     @Throws(BluePrintException::class)\r
162     open fun validateInputs(inputs: MutableMap<String, PropertyDefinition>) {\r
163         paths.add("inputs")\r
164         message.appendln("---> Input :" + paths.joinToString(separator))\r
165         validatePropertyDefinitions(inputs)\r
166         paths.removeAt(paths.lastIndex)\r
167     }\r
168 \r
169     @Throws(BluePrintException::class)\r
170     open fun validateNodeTemplates(nodeTemplates: MutableMap<String, NodeTemplate>) {\r
171         paths.add("nodeTemplates")\r
172         nodeTemplates.forEach { nodeTemplateName, nodeTemplate ->\r
173             validateNodeTemplate(nodeTemplateName, nodeTemplate)\r
174         }\r
175         paths.removeAt(paths.lastIndex)\r
176     }\r
177 \r
178     @Throws(BluePrintException::class)\r
179     open fun validateNodeTemplate(nodeTemplateName: String, nodeTemplate: NodeTemplate) {\r
180         paths.add(nodeTemplateName)\r
181         message.appendln("---> Node Template :" + paths.joinToString(separator))\r
182         val type: String = nodeTemplate.type\r
183 \r
184         val nodeType: NodeType = serviceTemplate.nodeTypes?.get(type)\r
185                 ?: throw BluePrintException(format("Failed to get node type definition  for node template : {}", nodeTemplateName))\r
186 \r
187         nodeTemplate.artifacts?.let { validateArtifactDefinitions(nodeTemplate.artifacts!!) }\r
188         nodeTemplate.properties?.let { validatePropertyAssignments(nodeType.properties!!, nodeTemplate.properties!!) }\r
189         nodeTemplate.capabilities?.let { validateCapabilityAssignments(nodeTemplate.capabilities!!) }\r
190         nodeTemplate.requirements?.let { validateRequirementAssignments(nodeTemplate.requirements!!) }\r
191         nodeTemplate.interfaces?.let { validateInterfaceAssignments(nodeType, nodeTemplateName, nodeTemplate) }\r
192         paths.removeAt(paths.lastIndex)\r
193     }\r
194 \r
195     @Throws(BluePrintException::class)\r
196     open fun validateArtifactDefinitions(artifacts: MutableMap<String, ArtifactDefinition>) {\r
197         paths.add("artifacts")\r
198         artifacts.forEach { artifactDefinitionName, artifactDefinition ->\r
199             paths.add(artifactDefinitionName)\r
200             message.appendln("Validating artifact " + paths.joinToString(separator))\r
201             val type: String = artifactDefinition.type\r
202                     ?: throw BluePrintException("type is missing for artifact definition :" + artifactDefinitionName)\r
203             // Check Artifact Type\r
204             checkValidArtifactType(artifactDefinitionName, type)\r
205 \r
206             val file: String = artifactDefinition.file\r
207                     ?: throw BluePrintException(format("file is missing for artifact definition : {}", artifactDefinitionName))\r
208 \r
209             paths.removeAt(paths.lastIndex)\r
210         }\r
211         paths.removeAt(paths.lastIndex)\r
212     }\r
213 \r
214     @Throws(BluePrintException::class)\r
215     open fun validateWorkFlows(workflows: MutableMap<String, Workflow>) {\r
216         paths.add("workflows")\r
217         workflows.forEach { workflowName, workflow ->\r
218 \r
219             // Validate Single workflow\r
220             validateWorkFlow(workflowName, workflow)\r
221         }\r
222         paths.removeAt(paths.lastIndex)\r
223     }\r
224 \r
225     @Throws(BluePrintException::class)\r
226     open fun validateWorkFlow(workflowName: String, workflow: Workflow) {\r
227         paths.add(workflowName)\r
228         message.appendln("---> Workflow :" + paths.joinToString(separator))\r
229         // Step Validation Start\r
230         paths.add("steps")\r
231         workflow.steps?.forEach { stepName, step ->\r
232             paths.add(stepName)\r
233             message.appendln("----> Steps :" + paths.joinToString(separator))\r
234             paths.removeAt(paths.lastIndex)\r
235         }\r
236         paths.removeAt(paths.lastIndex)\r
237         // Step Validation Ends\r
238         paths.removeAt(paths.lastIndex)\r
239     }\r
240 \r
241     @Throws(BluePrintException::class)\r
242     open fun validatePropertyDefinitions(properties: MutableMap<String, PropertyDefinition>) {\r
243         paths.add("properties")\r
244         properties.forEach { propertyName, propertyDefinition ->\r
245             paths.add(propertyName)\r
246             val dataType: String = propertyDefinition.type\r
247             when {\r
248                 BluePrintTypes.validPrimitiveTypes().contains(dataType) -> {\r
249                     // Do Nothing\r
250                 }\r
251                 BluePrintTypes.validCollectionTypes().contains(dataType) -> {\r
252                     val entrySchemaType: String = propertyDefinition.entrySchema?.type\r
253                             ?: throw BluePrintException(format("Entry schema for data type ({}) for the property ({}) not found", dataType, propertyName))\r
254                     checkPrimitiveOrComplex(entrySchemaType, propertyName)\r
255                 }\r
256                 else -> checkPropertyDataType(dataType, propertyName)\r
257             }\r
258             message.appendln("property " + paths.joinToString(separator) + " of type " + dataType)\r
259             paths.removeAt(paths.lastIndex)\r
260         }\r
261         paths.removeAt(paths.lastIndex)\r
262     }\r
263 \r
264     @Throws(BluePrintException::class)\r
265     open fun validatePropertyAssignments(nodeTypeProperties: MutableMap<String, PropertyDefinition>,\r
266                                          properties: MutableMap<String, JsonNode>) {\r
267         properties.forEach { propertyName, propertyAssignment ->\r
268             val propertyDefinition: PropertyDefinition = nodeTypeProperties[propertyName]\r
269                     ?: throw BluePrintException(format("failed to get definition for the property ({})", propertyName))\r
270 \r
271             validatePropertyAssignment(propertyName, propertyDefinition, propertyAssignment)\r
272 \r
273         }\r
274     }\r
275 \r
276     @Throws(BluePrintException::class)\r
277     open fun validatePropertyAssignment(propertyName: String, propertyDefinition: PropertyDefinition,\r
278                                         propertyAssignment: JsonNode) {\r
279         // Check and Validate if Expression Node\r
280         val expressionData = BluePrintExpressionService.getExpressionData(propertyAssignment)\r
281         if (!expressionData.isExpression) {\r
282             checkPropertyValue(propertyName, propertyDefinition, propertyAssignment)\r
283         }\r
284     }\r
285 \r
286     @Throws(BluePrintException::class)\r
287     open fun validateCapabilityAssignments(capabilities: MutableMap<String, CapabilityAssignment>) {\r
288 \r
289     }\r
290 \r
291     @Throws(BluePrintException::class)\r
292     open fun validateRequirementAssignments(requirements: MutableMap<String, RequirementAssignment>) {\r
293 \r
294     }\r
295 \r
296     @Throws(BluePrintException::class)\r
297     open fun validateInterfaceAssignments(nodeType: NodeType, nodeTemplateName: String, nodeTemplate: NodeTemplate) {\r
298 \r
299         val interfaces = nodeTemplate.interfaces\r
300         paths.add("interfaces")\r
301         interfaces?.forEach { interfaceAssignmentName, interfaceAssignment ->\r
302             paths.add(interfaceAssignmentName)\r
303             val interfaceDefinition = nodeType.interfaces?.get(interfaceAssignmentName)\r
304                     ?: throw BluePrintException(format("Failed to get nodeTemplate({}) interface definition ({}) from" +\r
305                             " node type ({}) ", nodeTemplateName, interfaceAssignmentName, nodeTemplate.type))\r
306 \r
307             validateInterfaceAssignment(nodeTemplateName, interfaceAssignmentName, interfaceDefinition,\r
308                     interfaceAssignment)\r
309             paths.removeAt(paths.lastIndex)\r
310         }\r
311         paths.removeAt(paths.lastIndex)\r
312 \r
313 \r
314     }\r
315 \r
316     @Throws(BluePrintException::class)\r
317     open fun validateInterfaceAssignment(nodeTemplateName: String, interfaceAssignmentName: String,\r
318                                          interfaceDefinition: InterfaceDefinition,\r
319                                          interfaceAssignment: InterfaceAssignment) {\r
320 \r
321         val operations = interfaceAssignment.operations\r
322         operations?.let {\r
323             validateInterfaceOperationsAssignment(nodeTemplateName, interfaceAssignmentName, interfaceDefinition,\r
324                     interfaceAssignment)\r
325         }\r
326 \r
327     }\r
328 \r
329     @Throws(BluePrintException::class)\r
330     open fun validateInterfaceOperationsAssignment(nodeTemplateName: String, interfaceAssignmentName: String,\r
331                                                    interfaceDefinition: InterfaceDefinition,\r
332                                                    interfaceAssignment: InterfaceAssignment) {\r
333 \r
334         val operations = interfaceAssignment.operations\r
335         operations?.let {\r
336             it.forEach { operationAssignmentName, operationAssignments ->\r
337 \r
338                 val operationDefinition = interfaceDefinition.operations?.get(operationAssignmentName)\r
339                         ?: throw BluePrintException(format("Failed to get nodeTemplate({}) operation definition ({}) ",\r
340                                 nodeTemplateName, operationAssignmentName))\r
341 \r
342                 log.info("Validation Node Template({}) Interface({}) Operation ({})", nodeTemplateName,\r
343                         interfaceAssignmentName, operationAssignmentName)\r
344 \r
345                 val inputs = operationAssignments.inputs\r
346                 val outputs = operationAssignments.outputs\r
347 \r
348                 inputs?.forEach { propertyName, propertyAssignment ->\r
349                     val propertyDefinition = operationDefinition.inputs?.get(propertyName)\r
350                             ?: throw BluePrintException(format("Failed to get nodeTemplate({}) operation definition ({}) " +\r
351                                     "property definition({})", nodeTemplateName, operationAssignmentName, propertyName))\r
352                     // Check the property values with property definition\r
353                     validatePropertyAssignment(propertyName, propertyDefinition, propertyAssignment)\r
354                 }\r
355 \r
356             }\r
357         }\r
358 \r
359     }\r
360 \r
361 \r
362     @Throws(BluePrintException::class)\r
363     open fun validateInterfaceDefinitions(interfaces: MutableMap<String, InterfaceDefinition>) {\r
364         paths.add("interfaces")\r
365         interfaces.forEach { interfaceName, interfaceDefinition ->\r
366             paths.add(interfaceName)\r
367             message.appendln("Validating : " + paths.joinToString(separator))\r
368             interfaceDefinition.operations?.let { validateOperationDefinitions(interfaceDefinition.operations!!) }\r
369             paths.removeAt(paths.lastIndex)\r
370         }\r
371         paths.removeAt(paths.lastIndex)\r
372     }\r
373 \r
374     @Throws(BluePrintException::class)\r
375     open fun validateOperationDefinitions(operations: MutableMap<String, OperationDefinition>) {\r
376         paths.add("operations")\r
377         operations.forEach { opertaionName, operationDefinition ->\r
378             paths.add(opertaionName)\r
379             message.appendln("Validating : " + paths.joinToString(separator))\r
380             operationDefinition.implementation?.let { validateImplementation(operationDefinition.implementation!!) }\r
381             operationDefinition.inputs?.let { validatePropertyDefinitions(operationDefinition.inputs!!) }\r
382             operationDefinition.outputs?.let { validatePropertyDefinitions(operationDefinition.outputs!!) }\r
383             paths.removeAt(paths.lastIndex)\r
384         }\r
385         paths.removeAt(paths.lastIndex)\r
386     }\r
387 \r
388     @Throws(BluePrintException::class)\r
389     open fun validateImplementation(implementation: Implementation) {\r
390         checkNotEmptyNThrow(implementation.primary)\r
391     }\r
392 \r
393     @Throws(BluePrintException::class)\r
394     open fun checkValidArtifactType(artifactDefinitionName: String, artifactTypeName: String) {\r
395 \r
396         val artifactType = serviceTemplate.artifactTypes?.get(artifactTypeName)\r
397                 ?: throw BluePrintException(format("Failed to artifact type for artifact definition : {}", artifactDefinitionName))\r
398 \r
399         checkValidArtifactTypeDerivedFrom(artifactTypeName, artifactType.derivedFrom)\r
400     }\r
401 \r
402     @Throws(BluePrintException::class)\r
403     open fun checkValidArtifactTypeDerivedFrom(artifactTypeName: String, derivedFrom: String) {\r
404         check(BluePrintTypes.validArtifactTypeDerivedFroms.contains(derivedFrom)) {\r
405             throw BluePrintException(format("Failed to get artifact type ({})'s  derived from({}) definition ", artifactTypeName, derivedFrom))\r
406         }\r
407     }\r
408 \r
409     @Throws(BluePrintException::class)\r
410     open fun checkValidDataTypeDerivedFrom(dataTypeName: String, derivedFrom: String) {\r
411         check(BluePrintTypes.validDataTypeDerivedFroms.contains(derivedFrom)) {\r
412             throw BluePrintException(format("Failed to get data type ({})'s  derived from({}) definition ", dataTypeName, derivedFrom))\r
413         }\r
414     }\r
415 \r
416     open fun checkPropertyValue(propertyName: String, propertyDefinition: PropertyDefinition, propertyAssignment: JsonNode) {\r
417         val propertyType = propertyDefinition.type\r
418         val isValid: Boolean\r
419 \r
420         if (BluePrintTypes.validPrimitiveTypes().contains(propertyType)) {\r
421             isValid = JacksonUtils.checkJsonNodeValueOfPrimitiveType(propertyType, propertyAssignment)\r
422 \r
423         } else if (BluePrintTypes.validCollectionTypes().contains(propertyType)) {\r
424 \r
425             val entrySchemaType = propertyDefinition.entrySchema?.type\r
426                     ?: throw BluePrintException(format("Failed to get Entry Schema type for the collection property ({})", propertyName))\r
427 \r
428             if (!BluePrintTypes.validPropertyTypes().contains(entrySchemaType)) {\r
429                 checkPropertyDataType(entrySchemaType, propertyName)\r
430             }\r
431             isValid = JacksonUtils.checkJsonNodeValueOfCollectionType(propertyType, propertyAssignment)\r
432         } else {\r
433             checkPropertyDataType(propertyType, propertyName)\r
434             isValid = true\r
435         }\r
436 \r
437         check(isValid) {\r
438             throw BluePrintException(format("property({}) defined of type({}) is not compatable with the value ({})",\r
439                     propertyName, propertyType, propertyAssignment))\r
440         }\r
441     }\r
442 \r
443     private fun checkPropertyDataType(dataType: String, propertyName: String) {\r
444 \r
445         val dataType = serviceTemplate.dataTypes?.get(dataType)\r
446                 ?: throw BluePrintException(format("Data type ({}) for the property ({}) not found", dataType, propertyName))\r
447 \r
448         checkValidDataTypeDerivedFrom(propertyName, dataType.derivedFrom)\r
449 \r
450     }\r
451 \r
452     private fun checkPrimitiveOrComplex(dataType: String, propertyName: String): Boolean {\r
453         if (BluePrintTypes.validPrimitiveTypes().contains(dataType) || checkDataType(dataType)) {\r
454             return true\r
455         } else {\r
456             throw BluePrintException(format("Data type ({}) for the property ({}) is not valid", dataType))\r
457         }\r
458     }\r
459 \r
460     private fun checkDataType(key: String): Boolean {\r
461         return serviceTemplate.dataTypes?.containsKey(key) ?: false\r
462     }\r
463 \r
464 }