package alien4cloud.topology.validation;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import org.alien4cloud.tosca.catalog.index.IToscaTypeSearchService;
import org.alien4cloud.tosca.model.CSARDependency;
import org.alien4cloud.tosca.model.definitions.RequirementDefinition;
import org.alien4cloud.tosca.model.templates.NodeTemplate;
import org.alien4cloud.tosca.model.templates.RelationshipTemplate;
import org.alien4cloud.tosca.model.templates.Requirement;
import org.alien4cloud.tosca.model.templates.Topology;
import org.alien4cloud.tosca.model.types.NodeType;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.common.collect.Lists;
import org.springframework.stereotype.Component;
import alien4cloud.exception.NotFoundException;
import alien4cloud.topology.task.RequirementToSatisfy;
import alien4cloud.topology.task.RequirementsTask;
import alien4cloud.topology.task.TaskCode;
/**
* Performs validation of the requirements and capabilities bounds.
*/
@Component
public class TopologyRequirementBoundsValidationServices {
@Resource
private IToscaTypeSearchService toscaTypeSearchService;
/**
* Check if the upperBound of a requirement is reached on a node template
*
* @param nodeTemplate the node to check for requirement bound
* @param requirementName the name of the requirement
* @param dependencies the dependencies of the topology
* @return true if requirement upper bound is reached, false otherwise
*/
public boolean isRequirementUpperBoundReachedForSource(NodeTemplate nodeTemplate, String requirementName, Set<CSARDependency> dependencies) {
NodeType relatedIndexedNodeType = toscaTypeSearchService.getRequiredElementInDependencies(NodeType.class, nodeTemplate.getType(), dependencies);
Requirement requirement = nodeTemplate.getRequirements().get(requirementName);
if (nodeTemplate.getRelationships() == null || nodeTemplate.getRelationships().isEmpty()) {
return false;
}
RequirementDefinition requirementDefinition = getRequirementDefinition(relatedIndexedNodeType.getRequirements(), requirementName,
requirement.getType());
if (requirementDefinition.getUpperBound() == Integer.MAX_VALUE) {
return false;
}
int count = countRelationshipsForRequirement(requirementName, requirement.getType(), nodeTemplate.getRelationships());
return count >= requirementDefinition.getUpperBound();
}
/**
* Perform validation of requirements bounds/occurences for the given topology.
*
* @param topology The topology to check
* @return A list of validation errors (tasks to be done to make the topology compliant).
*/
public List<RequirementsTask> validateRequirementsLowerBounds(Topology topology) {
List<RequirementsTask> toReturnTaskList = Lists.newArrayList();
Map<String, NodeTemplate> nodeTemplates = topology.getNodeTemplates();
for (Map.Entry<String, NodeTemplate> nodeTempEntry : nodeTemplates.entrySet()) {
NodeTemplate nodeTemp = nodeTempEntry.getValue();
if (nodeTemp.getRequirements() == null) {
continue;
}
NodeType relatedIndexedNodeType = toscaTypeSearchService.getRequiredElementInDependencies(NodeType.class, nodeTemp.getType(),
topology.getDependencies());
// do pass if abstract node
if (relatedIndexedNodeType.isAbstract()) {
continue;
}
RequirementsTask task = new RequirementsTask();
task.setNodeTemplateName(nodeTempEntry.getKey());
task.setCode(TaskCode.SATISFY_LOWER_BOUND);
task.setComponent(relatedIndexedNodeType);
task.setRequirementsToImplement(Lists.<RequirementToSatisfy> newArrayList());
if (CollectionUtils.isNotEmpty(relatedIndexedNodeType.getRequirements())) {
for (RequirementDefinition reqDef : relatedIndexedNodeType.getRequirements()) {
int count = countRelationshipsForRequirement(reqDef.getId(), reqDef.getType(), nodeTemp.getRelationships());
if (count < reqDef.getLowerBound()) {
task.getRequirementsToImplement().add(new RequirementToSatisfy(reqDef.getId(), reqDef.getType(), reqDef.getLowerBound() - count));
continue;
}
}
if (CollectionUtils.isNotEmpty(task.getRequirementsToImplement())) {
toReturnTaskList.add(task);
}
}
}
return toReturnTaskList.isEmpty() ? null : toReturnTaskList;
}
/**
* Get the number of relationships from a node template that are actually linked to the given requirement.
*
* @param requirementName Name of the requirement for which to count relationships
* @param requirementType Type of the requirement for which to count relationships
* @param relationships Relationships connected to the node that holds the requirement.
* @return The number of relationships connected to the given requirement.
*/
private int countRelationshipsForRequirement(String requirementName, String requirementType, Map<String, RelationshipTemplate> relationships) {
int count = 0;
if (relationships == null) {
return 0;
}
for (Map.Entry<String, RelationshipTemplate> relEntry : relationships.entrySet()) {
if (relEntry.getValue().getRequirementName().equals(requirementName) && relEntry.getValue().getRequirementType().equals(requirementType)) {
count++;
}
}
return count;
}
private RequirementDefinition getRequirementDefinition(Collection<RequirementDefinition> requirementDefinitions, String requirementName,
String requirementType) {
for (RequirementDefinition requirementDef : requirementDefinitions) {
if (requirementDef.getId().equals(requirementName) && requirementDef.getType().equals(requirementType)) {
return requirementDef;
}
}
throw new NotFoundException("Requirement definition [" + requirementName + ":" + requirementType + "] cannot be found");
}
}