package alien4cloud.topology.validation; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.inject.Inject; import org.alien4cloud.tosca.catalog.index.IToscaTypeSearchService; import org.alien4cloud.tosca.model.definitions.AbstractPropertyValue; import org.alien4cloud.tosca.model.definitions.CapabilityDefinition; import org.alien4cloud.tosca.model.definitions.FilterDefinition; import org.alien4cloud.tosca.model.definitions.FunctionPropertyValue; import org.alien4cloud.tosca.model.definitions.NodeFilter; import org.alien4cloud.tosca.model.definitions.PropertyConstraint; import org.alien4cloud.tosca.model.definitions.PropertyDefinition; import org.alien4cloud.tosca.model.definitions.RequirementDefinition; import org.alien4cloud.tosca.model.definitions.ScalarPropertyValue; import org.alien4cloud.tosca.model.templates.NodeTemplate; import org.alien4cloud.tosca.model.templates.RelationshipTemplate; import org.alien4cloud.tosca.model.templates.Topology; import org.alien4cloud.tosca.model.types.CapabilityType; import org.alien4cloud.tosca.model.types.NodeType; import org.springframework.stereotype.Component; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import alien4cloud.paas.function.FunctionEvaluator; import alien4cloud.rest.model.RestErrorCode; import alien4cloud.topology.TopologyServiceCore; import alien4cloud.topology.task.NodeFilterConstraintViolation; import alien4cloud.topology.task.NodeFilterToSatisfy; import alien4cloud.topology.task.NodeFilterToSatisfy.Violations; import alien4cloud.topology.task.NodeFiltersTask; import alien4cloud.topology.task.TaskCode; import alien4cloud.tosca.normative.IPropertyType; import alien4cloud.tosca.normative.ToscaType; import alien4cloud.tosca.properties.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException; import alien4cloud.tosca.properties.constraints.exception.ConstraintViolationException; /** * Performs validation of node filters for all relationship of topology. */ @Component public class NodeFilterValidationService { @Inject private IToscaTypeSearchService toscaTypeSearchService; @Resource private TopologyServiceCore topologyServiceCore; private Map<String, RequirementDefinition> getRequirementsAsMap(NodeType nodeType) { Map<String, RequirementDefinition> requirementDefinitionMap = Maps.newHashMap(); for (RequirementDefinition definition : nodeType.getRequirements()) { requirementDefinitionMap.put(definition.getId(), definition); } return requirementDefinitionMap; } public List<NodeFiltersTask> validateStaticRequirementFilters(Topology topology) { return validateRequirementFilters(topology, true); } public List<NodeFiltersTask> validateAllRequirementFilters(Topology topology) { return validateRequirementFilters(topology, false); } /** * Performs validation of the node filters to check that relationships targets the filter requirements. */ private List<NodeFiltersTask> validateRequirementFilters(Topology topology, boolean skipInputs) { List<NodeFiltersTask> toReturnTaskList = Lists.newArrayList(); Map<String, NodeTemplate> nodeTemplates = topology.getNodeTemplates(); Map<String, NodeType> nodeTypes = topologyServiceCore.getIndexedNodeTypesFromTopology(topology, false, true, true); Map<String, CapabilityType> capabilityTypes = topologyServiceCore.getIndexedCapabilityTypesFromTopology(topology); for (Map.Entry<String, NodeTemplate> nodeTempEntry : nodeTemplates.entrySet()) { Map<String, RelationshipTemplate> relationshipsMap = nodeTempEntry.getValue().getRelationships(); if (relationshipsMap == null || relationshipsMap.isEmpty()) { continue; } NodeType sourceNodeType = toscaTypeSearchService.getRequiredElementInDependencies(NodeType.class, nodeTempEntry.getValue().getType(), topology.getDependencies()); if (sourceNodeType.isAbstract()) { continue; } NodeFiltersTask task = new NodeFiltersTask(); task.setNodeTemplateName(nodeTempEntry.getKey()); task.setCode(TaskCode.NODE_FILTER_INVALID); task.setComponent(sourceNodeType); task.setNodeFiltersToSatisfy(Lists.<NodeFilterToSatisfy> newArrayList()); validateFiltersForNode(sourceNodeType, relationshipsMap, topology, nodeTypes, capabilityTypes, task, skipInputs); if (!task.getNodeFiltersToSatisfy().isEmpty()) { toReturnTaskList.add(task); } } return toReturnTaskList.isEmpty() ? null : toReturnTaskList; } private void validateFiltersForNode(NodeType sourceNodeType, Map<String, RelationshipTemplate> relationshipsMap, Topology topology, Map<String, NodeType> nodeTypes, Map<String, CapabilityType> capabilityTypes, NodeFiltersTask task, boolean skipInputs) { Map<String, RequirementDefinition> requirementDefinitionMap = getRequirementsAsMap(sourceNodeType); for (Map.Entry<String, RelationshipTemplate> relationshipEntry : relationshipsMap.entrySet()) { RequirementDefinition requirementDefinition = requirementDefinitionMap.get(relationshipEntry.getValue().getRequirementName()); NodeFilter nodeFilter = requirementDefinition.getNodeFilter(); if (nodeFilter != null) { NodeTemplate targetNode = topology.getNodeTemplates().get(relationshipEntry.getValue().getTarget()); NodeType targetType = nodeTypes.get(relationshipEntry.getValue().getTarget()); NodeFilterToSatisfy nodeFilterToSatisfy = new NodeFilterToSatisfy(); nodeFilterToSatisfy.setRelationshipName(relationshipEntry.getKey()); nodeFilterToSatisfy.setTargetName(targetNode.getName()); validateNodeFilter(nodeFilter, targetNode, targetType, capabilityTypes, nodeFilterToSatisfy, skipInputs); if (!nodeFilterToSatisfy.getViolations().isEmpty() || !nodeFilterToSatisfy.getMissingCapabilities().isEmpty()) { task.getNodeFiltersToSatisfy().add(nodeFilterToSatisfy); } } } } private void validateNodeFilter(NodeFilter nodeFilter, NodeTemplate target, NodeType targetType, Map<String, CapabilityType> capabilityTypes, NodeFilterToSatisfy nodeFilterToSatisfy, boolean skipInputs) { List<Violations> violations = validateNodeFilterProperties(nodeFilter, target, targetType, skipInputs); nodeFilterToSatisfy.setViolations(violations); validateNodeFilterCapabilities(nodeFilter, target, targetType, capabilityTypes, nodeFilterToSatisfy, skipInputs); } private List<Violations> validateNodeFilterProperties(NodeFilter nodeFilter, NodeTemplate target, NodeType targetType, boolean skipInputs) { if (nodeFilter.getProperties() == null || nodeFilter.getProperties().isEmpty()) { return null; } Map<String, List<PropertyConstraint>> propertyFilters = nodeFilter.getProperties(); Map<String, AbstractPropertyValue> propertyValues = target.getProperties(); return validatePropertyFilters(propertyFilters, propertyValues, targetType.getProperties(), skipInputs); } private List<Violations> validatePropertyFilters(Map<String, List<PropertyConstraint>> propertyFilters, Map<String, AbstractPropertyValue> propertyValues, Map<String, PropertyDefinition> propertyDefinitionMap, boolean skipInputs) { List<Violations> violations = Lists.newArrayList(); for (Map.Entry<String, List<PropertyConstraint>> propertyEntry : propertyFilters.entrySet()) { Violations violation = new Violations(propertyEntry.getKey()); List<NodeFilterConstraintViolation> violatedConstraints = Lists.newArrayList(); violation.violatedConstraints = violatedConstraints; AbstractPropertyValue value = propertyValues.get(propertyEntry.getKey()); String propertyValue = null; if (value == null) { propertyValue = null; } else if (value instanceof ScalarPropertyValue) { propertyValue = ((ScalarPropertyValue) value).getValue(); } else { if (skipInputs) { continue; } if (FunctionEvaluator.isGetInput((FunctionPropertyValue) value)) { violation.relatedInput = ((FunctionPropertyValue) value).getElementNameToFetch(); } } for (PropertyConstraint constraint : propertyEntry.getValue()) { if (!propertyDefinitionMap.containsKey(propertyEntry.getKey())) { continue; } // the constraint need to be initiazed with the type of the property (to check that actual value type matches the definition type). IPropertyType<?> toscaType = ToscaType.fromYamlTypeName(propertyDefinitionMap.get(propertyEntry.getKey()).getType()); try { constraint.initialize(toscaType); constraint.validate(toscaType, propertyValue); } catch (ConstraintViolationException e) { violatedConstraints.add( new NodeFilterConstraintViolation(RestErrorCode.PROPERTY_CONSTRAINT_VIOLATION_ERROR, e.getMessage(), e.getConstraintInformation())); } catch (ConstraintValueDoNotMatchPropertyTypeException e) { violatedConstraints.add(new NodeFilterConstraintViolation(RestErrorCode.PROPERTY_TYPE_VIOLATION_ERROR, e.getMessage(), null)); } } if (!violatedConstraints.isEmpty()) { violations.add(violation); } } return violations; } private void validateNodeFilterCapabilities(NodeFilter nodeFilter, NodeTemplate target, NodeType targetType, Map<String, CapabilityType> capabilityTypes, NodeFilterToSatisfy nodeFilterToSatisfy, boolean skipInputs) { nodeFilterToSatisfy.setMissingCapabilities(Lists.<String> newArrayList()); if (nodeFilter.getCapabilities() == null || nodeFilter.getCapabilities().isEmpty()) { return; } Map<String, FilterDefinition> capabilities = nodeFilter.getCapabilities(); for (Map.Entry<String, FilterDefinition> filterDefinitionEntry : capabilities.entrySet()) { String capabilityName = filterDefinitionEntry.getKey(); CapabilityDefinition definition = getCapabilityDefinition(targetType, capabilityName); if (definition == null) { nodeFilterToSatisfy.getMissingCapabilities().add(capabilityName); continue; } CapabilityType capabilityType = capabilityTypes.get(definition.getType()); List<Violations> violations = validatePropertyFilters(filterDefinitionEntry.getValue().getProperties(), target.getCapabilities().get(definition.getId()).getProperties(), capabilityType.getProperties(), skipInputs); if (nodeFilterToSatisfy.getViolations() == null) { nodeFilterToSatisfy.setViolations(Lists.<Violations> newArrayList()); } nodeFilterToSatisfy.getViolations().addAll(violations); } } private CapabilityDefinition getCapabilityDefinition(NodeType targetType, String filterCapabilityKey) { for (CapabilityDefinition capabilityDefinition : targetType.getCapabilities()) { if (filterCapabilityKey.equals(capabilityDefinition.getId()) || filterCapabilityKey.equals(capabilityDefinition.getType())) { return capabilityDefinition; } } return null; } }