package org.alien4cloud.tosca.editor.services; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.annotation.Resource; import alien4cloud.tosca.normative.ToscaType; import org.alien4cloud.tosca.catalog.ArchiveDelegateType; import org.alien4cloud.tosca.catalog.index.CsarService; import org.alien4cloud.tosca.catalog.index.ICsarDependencyLoader; import org.alien4cloud.tosca.catalog.index.IToscaTypeIndexerService; import org.alien4cloud.tosca.model.Csar; import org.alien4cloud.tosca.model.definitions.AttributeDefinition; import org.alien4cloud.tosca.model.definitions.CapabilityDefinition; import org.alien4cloud.tosca.model.definitions.IValue; 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.SubstitutionTarget; import org.alien4cloud.tosca.model.templates.Topology; import org.alien4cloud.tosca.model.types.CapabilityType; import org.alien4cloud.tosca.model.types.NodeType; import org.elasticsearch.common.collect.Lists; import org.springframework.stereotype.Service; import com.google.common.collect.Maps; import alien4cloud.model.components.IndexedModelUtils; import alien4cloud.topology.TopologyServiceCore; import alien4cloud.tosca.context.ToscaContext; import alien4cloud.tosca.context.ToscaContextual; import lombok.extern.slf4j.Slf4j; /** * Manages topology substitutions */ @Service @Slf4j public class TopologySubstitutionService { @Resource private CsarService csarService; @Resource private IToscaTypeIndexerService indexerService; @Resource private ICsarDependencyLoader csarDependencyLoader; @ToscaContextual public void updateSubstitutionType(final Topology topology, Csar csar) { // FIXME we do not yet support substitution from application topology if (Objects.equals(csar.getDelegateType(), ArchiveDelegateType.APPLICATION)) { return; } if (topology.getSubstitutionMapping() == null || topology.getSubstitutionMapping().getSubstitutionType() == null) { return; } // first we update the csar and the dependencies NodeType nodeType = ToscaContext.getOrFail(NodeType.class, topology.getSubstitutionMapping().getSubstitutionType().getElementId()); csar.getDependencies().add(csarDependencyLoader.buildDependencyBean(nodeType.getArchiveName(), nodeType.getArchiveVersion())); // FIXME manage hash for substitution elements too (should we just generate based on type). // csar.setHash("-1"); csarService.save(csar); // We create the nodeType that will serve as substitute NodeType substituteNodeType = buildSubstituteNodeType(topology, csar, nodeType); // inputs from topology become properties of type substituteNodeType.setProperties(topology.getInputs()); // output attributes become attributes for the type fillSubstituteAttributesFromTypeAtttributes(topology, substituteNodeType); // output properties become attributes for the type fillSubstituteAttributesFromOutputProperties(topology, substituteNodeType); // output capabilities properties also become attributes for the type fillAttributesFromOutputCapabilitiesProperties(topology, substituteNodeType); // capabilities substitution fillCapabilities(topology, substituteNodeType); // requirement substitution fillRequirements(topology, substituteNodeType); // finally we index the created type indexerService.indexInheritableElement(csar.getName(), csar.getVersion(), substituteNodeType, csar.getDependencies()); } private void fillRequirements(Topology topology, NodeType substituteNodeType) { if (topology.getSubstitutionMapping().getRequirements() != null) { for (Map.Entry<String, SubstitutionTarget> e : topology.getSubstitutionMapping().getRequirements().entrySet()) { String key = e.getKey(); String nodeName = e.getValue().getNodeTemplateName(); String requirementName = e.getValue().getTargetId(); NodeTemplate nodeTemplate = topology.getNodeTemplates().get(nodeName); NodeType nodeTemplateType = ToscaContext.getOrFail(NodeType.class, nodeTemplate.getType()); RequirementDefinition requirementDefinition = IndexedModelUtils.getRequirementDefinitionById(nodeTemplateType.getRequirements(), requirementName); requirementDefinition.setId(key); substituteNodeType.getRequirements().add(requirementDefinition); } } } private void fillCapabilities(Topology topology, NodeType substituteNodeType) { if (topology.getSubstitutionMapping().getCapabilities() != null) { for (Map.Entry<String, SubstitutionTarget> e : topology.getSubstitutionMapping().getCapabilities().entrySet()) { String key = e.getKey(); String nodeName = e.getValue().getNodeTemplateName(); String capabilityName = e.getValue().getTargetId(); NodeTemplate nodeTemplate = topology.getNodeTemplates().get(nodeName); NodeType nodeTemplateType = ToscaContext.getOrFail(NodeType.class, nodeTemplate.getType()); CapabilityDefinition capabilityDefinition = IndexedModelUtils.getCapabilityDefinitionById(nodeTemplateType.getCapabilities(), capabilityName); capabilityDefinition.setId(key); substituteNodeType.getCapabilities().add(capabilityDefinition); } } } private void fillAttributesFromOutputCapabilitiesProperties(Topology topology, NodeType substituteNodeType) { Map<String, Map<String, Set<String>>> outputCapabilityProperties = topology.getOutputCapabilityProperties(); if (outputCapabilityProperties != null) { for (Map.Entry<String, Map<String, Set<String>>> ocpe : outputCapabilityProperties.entrySet()) { String nodeName = ocpe.getKey(); NodeTemplate nodeTemplate = topology.getNodeTemplates().get(nodeName); for (Map.Entry<String, Set<String>> cpe : ocpe.getValue().entrySet()) { String capabilityName = cpe.getKey(); String capabilityTypeName = nodeTemplate.getCapabilities().get(capabilityName).getType(); CapabilityType capabilityType = ToscaContext.getOrFail(CapabilityType.class, capabilityTypeName); for (String propertyName : cpe.getValue()) { PropertyDefinition pd = capabilityType.getProperties().get(propertyName); // there is a conflict addAttributeFromPropertyDefinition(pd, propertyName, substituteNodeType); } } } } } private void fillSubstituteAttributesFromOutputProperties(Topology topology, NodeType substituteNodeType) { Map<String, Set<String>> outputProperties = topology.getOutputProperties(); if (outputProperties != null) { for (Map.Entry<String, Set<String>> ope : outputProperties.entrySet()) { String nodeName = ope.getKey(); NodeTemplate nodeTemplate = topology.getNodeTemplates().get(nodeName); NodeType nodeTemplateType = ToscaContext.getOrFail(NodeType.class, nodeTemplate.getType()); for (String propertyName : ope.getValue()) { PropertyDefinition pd = nodeTemplateType.getProperties().get(propertyName); // is a conflict addAttributeFromPropertyDefinition(pd, propertyName, substituteNodeType); } } } } private void addAttributeFromPropertyDefinition(PropertyDefinition pd, String propertyName, NodeType substituteNodeType) { // FIXME we have an issue here : if several nodes have the same attribute name, or if an attribute and a property have the same name, Map<String, IValue> attributes = substituteNodeType.getAttributes(); if (pd != null && !attributes.containsKey(propertyName)) { if (ToscaType.isSimple(pd.getType())) { AttributeDefinition attributeDefinition = new AttributeDefinition(); attributeDefinition.setType(pd.getType()); attributeDefinition.setDescription(pd.getDescription()); // FIXME known issue we don't support complex attributes right now. if (pd.getDefault() != null && pd.getDefault() instanceof ScalarPropertyValue) { attributeDefinition.setDefault(((ScalarPropertyValue) pd.getDefault()).getValue()); } attributes.put(propertyName, attributeDefinition); } // FIXME else: known issue we don't support complex attributes right now. } } private void fillSubstituteAttributesFromTypeAtttributes(Topology topology, NodeType substituteNodeType) { Map<String, IValue> attributes = substituteNodeType.getAttributes(); Map<String, Set<String>> outputAttributes = topology.getOutputAttributes(); if (outputAttributes != null) { for (Map.Entry<String, Set<String>> oae : outputAttributes.entrySet()) { String nodeName = oae.getKey(); NodeTemplate nodeTemplate = TopologyServiceCore.getNodeTemplate(topology, nodeName); NodeType nodeTemplateType = ToscaContext.getOrFail(NodeType.class, nodeTemplate.getType()); for (String attributeName : oae.getValue()) { IValue ivalue = nodeTemplateType.getAttributes().get(attributeName); // FIXME we have an issue here : if several nodes have the same attribute name, or if an attribute and a property have the same name, there // is a conflict if (ivalue != null && !attributes.containsKey(attributeName)) { attributes.put(attributeName, ivalue); } } } } } private NodeType buildSubstituteNodeType(Topology topology, Csar csar, NodeType nodeType) { NodeType substituteNodeType = new NodeType(); substituteNodeType.setArchiveName(csar.getName()); substituteNodeType.setArchiveVersion(csar.getVersion()); substituteNodeType.setElementId(csar.getName()); substituteNodeType.setDerivedFrom(Lists.newArrayList(nodeType.getElementId())); substituteNodeType.setSubstitutionTopologyId(topology.getId()); substituteNodeType.setWorkspace(topology.getWorkspace()); List<CapabilityDefinition> capabilities = Lists.newArrayList(); substituteNodeType.setCapabilities(capabilities); List<RequirementDefinition> requirements = Lists.newArrayList(); substituteNodeType.setRequirements(requirements); substituteNodeType.setAttributes(Maps.newHashMap()); return substituteNodeType; } }