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