Migrate "ms/controllerblueprints" from ccsdk/apps
[ccsdk/cds.git] / ms / controllerblueprints / modules / blueprint-validation / src / main / kotlin / org / onap / ccsdk / cds / controllerblueprints / validation / BluePrintNodeTemplateValidatorImpl.kt
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Modifications Copyright © 2018 IBM.
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.controllerblueprints.validation
19
20 import com.att.eelf.configuration.EELFLogger
21 import com.att.eelf.configuration.EELFManager
22 import com.fasterxml.jackson.databind.JsonNode
23 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
24 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintTypes
25 import org.onap.ccsdk.cds.controllerblueprints.core.data.*
26 import org.onap.ccsdk.cds.controllerblueprints.core.format
27 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintNodeTemplateValidator
28 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintTypeValidatorService
29 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext
30 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintExpressionService
31 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
32 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
33 import org.springframework.beans.factory.config.ConfigurableBeanFactory
34 import org.springframework.context.annotation.Scope
35 import org.springframework.stereotype.Service
36
37
38 @Service("default-node-template-validator")
39 @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
40 open class BluePrintNodeTemplateValidatorImpl(private val bluePrintTypeValidatorService: BluePrintTypeValidatorService) : BluePrintNodeTemplateValidator {
41
42     private val log: EELFLogger = EELFManager.getInstance().getLogger(BluePrintNodeTemplateValidatorImpl::class.toString())
43
44     lateinit var bluePrintRuntimeService: BluePrintRuntimeService<*>
45     lateinit var bluePrintContext: BluePrintContext
46     var paths: MutableList<String> = arrayListOf()
47
48     override fun validate(bluePrintRuntimeService: BluePrintRuntimeService<*>, name: String, nodeTemplate: NodeTemplate) {
49         log.info("Validating NodeTemplate($name)")
50
51         this.bluePrintRuntimeService = bluePrintRuntimeService
52         this.bluePrintContext = bluePrintRuntimeService.bluePrintContext()
53
54         paths.add(name)
55
56         val type: String = nodeTemplate.type
57
58         val nodeType: NodeType = bluePrintContext.serviceTemplate.nodeTypes?.get(type)
59                 ?: throw BluePrintException("Failed to get NodeType($type) definition for NodeTemplate($name)")
60
61         nodeTemplate.properties?.let { validatePropertyAssignments(nodeType.properties!!, nodeTemplate.properties!!) }
62         nodeTemplate.capabilities?.let { validateCapabilityAssignments(nodeType, name, nodeTemplate) }
63         nodeTemplate.requirements?.let { validateRequirementAssignments(nodeType, name, nodeTemplate) }
64         nodeTemplate.interfaces?.let { validateInterfaceAssignments(nodeType, name, nodeTemplate) }
65         nodeTemplate.artifacts?.let { validateArtifactDefinitions(nodeTemplate.artifacts!!) }
66
67         // Perform Extension Validation
68         validateExtension("$type-node-template-validator", name, nodeTemplate)
69
70         paths.removeAt(paths.lastIndex)
71     }
72
73     @Throws(BluePrintException::class)
74     open fun validateArtifactDefinitions(artifacts: MutableMap<String, ArtifactDefinition>) {
75         paths.add("artifacts")
76         artifacts.forEach { artifactDefinitionName, artifactDefinition ->
77             bluePrintTypeValidatorService.validateArtifactDefinition(bluePrintRuntimeService,
78                     artifactDefinitionName, artifactDefinition)
79         }
80         paths.removeAt(paths.lastIndex)
81     }
82
83
84     @Throws(BluePrintException::class)
85     open fun validatePropertyAssignments(nodeTypeProperties: MutableMap<String, PropertyDefinition>,
86                                          properties: MutableMap<String, JsonNode>) {
87         properties.forEach { propertyName, propertyAssignment ->
88             val propertyDefinition: PropertyDefinition = nodeTypeProperties[propertyName]
89                     ?: throw BluePrintException("failed to get definition for the property ($propertyName)")
90
91             validatePropertyAssignment(propertyName, propertyDefinition, propertyAssignment)
92
93         }
94     }
95
96     @Throws(BluePrintException::class)
97     open fun validatePropertyAssignment(propertyName: String, propertyDefinition: PropertyDefinition,
98                                         propertyAssignment: JsonNode) {
99         // Check and Validate if Expression Node
100         val expressionData = BluePrintExpressionService.getExpressionData(propertyAssignment)
101         if (!expressionData.isExpression) {
102             checkPropertyValue(propertyName, propertyDefinition, propertyAssignment)
103         }
104     }
105
106     @Throws(BluePrintException::class)
107     open fun validateCapabilityAssignments(nodeType: NodeType, nodeTemplateName: String, nodeTemplate: NodeTemplate) {
108         val capabilities = nodeTemplate.capabilities
109         paths.add("capabilities")
110         capabilities?.forEach { capabilityName, capabilityAssignment ->
111             paths.add(capabilityName)
112
113             val capabilityDefinition = nodeType.capabilities?.get(capabilityName)
114                     ?: throw BluePrintException("Failed to get NodeTemplate($nodeTemplateName) capability definition ($capabilityName) " +
115                             "from NodeType(${nodeTemplate.type})")
116
117             validateCapabilityAssignment(nodeTemplateName, capabilityName, capabilityDefinition, capabilityAssignment)
118
119             paths.removeAt(paths.lastIndex)
120         }
121         paths.removeAt(paths.lastIndex)
122     }
123
124     @Throws(BluePrintException::class)
125     open fun validateCapabilityAssignment(nodeTemplateName: String, capabilityName: String,
126                                           capabilityDefinition: CapabilityDefinition, capabilityAssignment: CapabilityAssignment) {
127
128         capabilityAssignment.properties?.let { validatePropertyAssignments(capabilityDefinition.properties!!, capabilityAssignment.properties!!) }
129
130     }
131
132     @Throws(BluePrintException::class)
133     open fun validateRequirementAssignments(nodeType: NodeType, nodeTemplateName: String, nodeTemplate: NodeTemplate) {
134         val requirements = nodeTemplate.requirements
135         paths.add("requirements")
136         requirements?.forEach { requirementName, requirementAssignment ->
137             paths.add(requirementName)
138             val requirementDefinition = nodeType.requirements?.get(requirementName)
139                     ?: throw BluePrintException("Failed to get NodeTemplate($nodeTemplateName) requirement definition ($requirementName) from" +
140                             " NodeType(${nodeTemplate.type})")
141             // Validate Requirement Assignment
142             validateRequirementAssignment(nodeTemplateName, requirementName, requirementDefinition, requirementAssignment)
143             paths.removeAt(paths.lastIndex)
144         }
145         paths.removeAt(paths.lastIndex)
146
147     }
148
149     @Throws(BluePrintException::class)
150     open fun validateRequirementAssignment(nodeTemplateName: String, requirementAssignmentName: String,
151                                            requirementDefinition: RequirementDefinition, requirementAssignment: RequirementAssignment) {
152         log.info("Validating NodeTemplate({}) requirement assignment ({}) ", nodeTemplateName, requirementAssignmentName)
153         val requirementNodeTemplateName = requirementAssignment.node!!
154         val capabilityName = requirementAssignment.capability
155         val relationship = requirementAssignment.relationship!!
156
157         check(BluePrintTypes.validRelationShipDerivedFroms.contains(relationship)) {
158             throw BluePrintException("Failed to get relationship type ($relationship) for NodeTemplate($nodeTemplateName)'s requirement($requirementAssignmentName)")
159         }
160
161         val relationShipNodeTemplate = bluePrintContext.serviceTemplate.topologyTemplate?.nodeTemplates?.get(requirementNodeTemplateName)
162                 ?: throw BluePrintException("Failed to get requirement NodeTemplate($requirementNodeTemplateName)'s " +
163                         "for NodeTemplate($nodeTemplateName) requirement($requirementAssignmentName)")
164
165         relationShipNodeTemplate.capabilities?.get(capabilityName)
166                 ?: throw BluePrintException("Failed to get requirement NodeTemplate($requirementNodeTemplateName)'s " +
167                         "capability($capabilityName) for NodeTemplate ($nodeTemplateName)'s requirement($requirementAssignmentName)")
168
169
170     }
171
172     @Throws(BluePrintException::class)
173     open fun validateInterfaceAssignments(nodeType: NodeType, nodeTemplateName: String, nodeTemplate: NodeTemplate) {
174
175         val interfaces = nodeTemplate.interfaces
176         paths.add("interfaces")
177         interfaces?.forEach { interfaceAssignmentName, interfaceAssignment ->
178             paths.add(interfaceAssignmentName)
179             val interfaceDefinition = nodeType.interfaces?.get(interfaceAssignmentName)
180                     ?: throw BluePrintException("Failed to get NodeTemplate($nodeTemplateName) interface definition ($interfaceAssignmentName) from" +
181                             " NodeType(${nodeTemplate.type})")
182
183             validateInterfaceAssignment(nodeTemplateName, interfaceAssignmentName, interfaceDefinition,
184                     interfaceAssignment)
185             paths.removeAt(paths.lastIndex)
186         }
187         paths.removeAt(paths.lastIndex)
188
189
190     }
191
192     @Throws(BluePrintException::class)
193     open fun validateInterfaceAssignment(nodeTemplateName: String, interfaceAssignmentName: String,
194                                          interfaceDefinition: InterfaceDefinition,
195                                          interfaceAssignment: InterfaceAssignment) {
196
197         val operations = interfaceAssignment.operations
198         operations?.let {
199             validateInterfaceOperationsAssignment(nodeTemplateName, interfaceAssignmentName, interfaceDefinition,
200                     interfaceAssignment)
201         }
202
203     }
204
205     @Throws(BluePrintException::class)
206     open fun validateInterfaceOperationsAssignment(nodeTemplateName: String, interfaceAssignmentName: String,
207                                                    interfaceDefinition: InterfaceDefinition,
208                                                    interfaceAssignment: InterfaceAssignment) {
209
210         val operations = interfaceAssignment.operations
211         operations?.let {
212             it.forEach { operationAssignmentName, operationAssignments ->
213
214                 val operationDefinition = interfaceDefinition.operations?.get(operationAssignmentName)
215                         ?: throw BluePrintException("Failed to get NodeTemplate($nodeTemplateName) operation definition ($operationAssignmentName)")
216
217                 log.info("Validation NodeTemplate($nodeTemplateName) Interface($interfaceAssignmentName) Operation ($operationAssignmentName)")
218
219                 val inputs = operationAssignments.inputs
220                 val outputs = operationAssignments.outputs
221
222                 inputs?.forEach { propertyName, propertyAssignment ->
223                     val propertyDefinition = operationDefinition.inputs?.get(propertyName)
224                             ?: throw BluePrintException("Failed to get NodeTemplate($nodeTemplateName) operation " +
225                                     "definition ($operationAssignmentName) property definition($propertyName)")
226                     // Check the property values with property definition
227                     validatePropertyAssignment(propertyName, propertyDefinition, propertyAssignment)
228                 }
229
230                 outputs?.forEach { propertyName, propertyAssignment ->
231                     val propertyDefinition = operationDefinition.outputs?.get(propertyName)
232                             ?: throw BluePrintException("Failed to get NodeTemplate($nodeTemplateName) operation definition ($operationAssignmentName) " +
233                                     "output property definition($propertyName)")
234                     // Check the property values with property definition
235                     validatePropertyAssignment(propertyName, propertyDefinition, propertyAssignment)
236                 }
237
238             }
239         }
240
241     }
242
243     open fun checkPropertyValue(propertyName: String, propertyDefinition: PropertyDefinition, propertyAssignment: JsonNode) {
244         val propertyType = propertyDefinition.type
245         val isValid: Boolean
246
247         if (BluePrintTypes.validPrimitiveTypes().contains(propertyType)) {
248             isValid = JacksonUtils.checkJsonNodeValueOfPrimitiveType(propertyType, propertyAssignment)
249
250         } else if (BluePrintTypes.validComplexTypes().contains(propertyType)) {
251             isValid = true
252         } else if (BluePrintTypes.validCollectionTypes().contains(propertyType)) {
253
254             val entrySchemaType = propertyDefinition.entrySchema?.type
255                     ?: throw BluePrintException(format("Failed to get EntrySchema type for the collection property ({})", propertyName))
256
257             if (!BluePrintTypes.validPropertyTypes().contains(entrySchemaType)) {
258                 checkPropertyDataType(entrySchemaType, propertyName)
259             }
260             isValid = JacksonUtils.checkJsonNodeValueOfCollectionType(propertyType, propertyAssignment)
261         } else {
262             checkPropertyDataType(propertyType, propertyName)
263             isValid = true
264         }
265
266         check(isValid) {
267             throw BluePrintException("property(propertyName) defined of type(propertyType) is not comptable with the value (propertyAssignment)")
268         }
269     }
270
271     private fun checkPropertyDataType(dataTypeName: String, propertyName: String) {
272
273         val dataType = bluePrintContext.serviceTemplate.dataTypes?.get(dataTypeName)
274                 ?: throw BluePrintException("DataType ($dataTypeName) for the property ($propertyName) not found")
275
276         checkValidDataTypeDerivedFrom(propertyName, dataType.derivedFrom)
277
278     }
279
280     private fun checkValidDataTypeDerivedFrom(dataTypeName: String, derivedFrom: String) {
281         check(BluePrintTypes.validDataTypeDerivedFroms.contains(derivedFrom)) {
282             throw BluePrintException("Failed to get DataType($dataTypeName)'s  derivedFrom($derivedFrom) definition ")
283         }
284     }
285
286     private fun validateExtension(referencePrefix: String, name: String, nodeTemplate: NodeTemplate) {
287         val customValidator = bluePrintTypeValidatorService
288                 .bluePrintValidator(referencePrefix, BluePrintNodeTemplateValidator::class.java)
289
290         customValidator?.let {
291             it.validate(bluePrintRuntimeService, name, nodeTemplate)
292         }
293     }
294
295 }