package alien4cloud.paas.wf; import java.util.List; import java.util.Map; import javax.annotation.Resource; 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.AbstractToscaType; import org.elasticsearch.common.collect.Lists; import org.elasticsearch.common.collect.Maps; import org.springframework.stereotype.Component; import alien4cloud.component.ICSARRepositorySearchService; import alien4cloud.exception.NotFoundException; import alien4cloud.paas.wf.exception.BadWorkflowOperationException; import alien4cloud.paas.wf.util.WorkflowUtils; import alien4cloud.paas.wf.validation.WorkflowValidator; import alien4cloud.topology.task.TaskCode; import alien4cloud.topology.task.WorkflowTask; import lombok.extern.slf4j.Slf4j; @Component @Slf4j public class WorkflowsBuilderService { @Resource private ICSARRepositorySearchService csarRepoSearchService; @Resource private InstallWorkflowBuilder installWorkflowBuilder; @Resource private UninstallWorkflowBuilder uninstallWorkflowBuilder; @Resource private CustomWorkflowBuilder customWorkflowBuilder; @Resource private WorkflowValidator workflowValidator; public TopologyContext initWorkflows(TopologyContext topologyContext) { Map<String, Workflow> wfs = topologyContext.getTopology().getWorkflows(); if (wfs == null) { wfs = Maps.newLinkedHashMap(); topologyContext.getTopology().setWorkflows(wfs); } if (!wfs.containsKey(Workflow.INSTALL_WF)) { Workflow install = new Workflow(); install.setStandard(true); install.setName(Workflow.INSTALL_WF); wfs.put(Workflow.INSTALL_WF, install); reinitWorkflow(Workflow.INSTALL_WF, topologyContext); } if (!wfs.containsKey(Workflow.UNINSTALL_WF)) { Workflow uninstall = new Workflow(); uninstall.setStandard(true); uninstall.setName(Workflow.UNINSTALL_WF); wfs.put(Workflow.UNINSTALL_WF, uninstall); reinitWorkflow(Workflow.UNINSTALL_WF, topologyContext); } return topologyContext; } public Workflow ceateWorkflow(Topology topology, String name) { String workflowName = getWorkflowName(topology, name, 0); Workflow wf = new Workflow(); wf.setName(workflowName); wf.setStandard(false); Map<String, Workflow> wfs = topology.getWorkflows(); if (wfs == null) { wfs = Maps.newLinkedHashMap(); topology.setWorkflows(wfs); } wfs.put(workflowName, wf); return wf; } private String getWorkflowName(Topology topology, String name, int index) { String workflowName = name; if (index > 0) { workflowName += "_" + index; } if (topology.getWorkflows() != null && topology.getWorkflows().containsKey(workflowName)) { return getWorkflowName(topology, name, ++index); } else { return workflowName; } } private void debugWorkflow(Topology topology) { if (log.isDebugEnabled()) { for (Workflow wf : topology.getWorkflows().values()) { log.debug(WorkflowUtils.debugWorkflow(wf)); } } } public int validateWorkflow(TopologyContext topologyContext, Workflow workflow) { return workflowValidator.validate(topologyContext, workflow); } public void addNode(TopologyContext topologyContext, String nodeName, NodeTemplate nodeTemplate) { boolean forceOperation = WorkflowUtils.isComputeOrNetwork(nodeName, topologyContext); for (Workflow wf : topologyContext.getTopology().getWorkflows().values()) { AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.addNode(wf, nodeName, topologyContext, forceOperation); WorkflowUtils.fillHostId(wf, topologyContext); workflowValidator.validate(topologyContext, wf); } debugWorkflow(topologyContext.getTopology()); } public void removeNode(Topology topology, String nodeName, NodeTemplate nodeTemplate) { TopologyContext topologyContext = buildTopologyContext(topology); for (Workflow wf : topology.getWorkflows().values()) { AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.removeNode(wf, nodeName); WorkflowUtils.fillHostId(wf, topologyContext); workflowValidator.validate(topologyContext, wf); } debugWorkflow(topology); } public void addRelationship(TopologyContext topologyContext, String nodeTemplateName, String relationshipName) { NodeTemplate nodeTemplate = topologyContext.getTopology().getNodeTemplates().get(nodeTemplateName); RelationshipTemplate relationshipTemplate = nodeTemplate.getRelationships().get(relationshipName); for (Workflow wf : topologyContext.getTopology().getWorkflows().values()) { AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.addRelationship(wf, nodeTemplateName, nodeTemplate, relationshipTemplate, topologyContext); WorkflowUtils.fillHostId(wf, topologyContext); workflowValidator.validate(topologyContext, wf); } debugWorkflow(topologyContext.getTopology()); } public void removeRelationship(Topology topology, String nodeTemplateName, String relationshipName, RelationshipTemplate relationshipTemplate) { TopologyContext topologyContext = buildTopologyContext(topology); String relationhipTarget = relationshipTemplate.getTarget(); for (Workflow wf : topology.getWorkflows().values()) { AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.removeRelationship(wf, nodeTemplateName, relationhipTarget); WorkflowUtils.fillHostId(wf, topologyContext); workflowValidator.validate(topologyContext, wf); } } public Workflow removeEdge(Topology topology, String workflowName, String from, String to) { TopologyContext topologyContext = buildTopologyContext(topology); Workflow wf = topology.getWorkflows().get(workflowName); if (wf == null) { throw new NotFoundException(String.format("The workflow '%s' can not be found", workflowName)); } AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.removeEdge(wf, from, to); workflowValidator.validate(topologyContext, wf); return wf; } public Workflow connectStepFrom(Topology topology, String workflowName, String stepId, String[] stepNames) { TopologyContext topologyContext = buildTopologyContext(topology); Workflow wf = topology.getWorkflows().get(workflowName); if (wf == null) { throw new NotFoundException(String.format("The workflow '%s' can not be found", workflowName)); } AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.connectStepFrom(wf, stepId, stepNames); workflowValidator.validate(topologyContext, wf); return wf; } public Workflow connectStepTo(Topology topology, String workflowName, String stepId, String[] stepNames) { TopologyContext topologyContext = buildTopologyContext(topology); Workflow wf = topology.getWorkflows().get(workflowName); if (wf == null) { throw new NotFoundException(String.format("The workflow '%s' can not be found", workflowName)); } AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.connectStepTo(wf, stepId, stepNames); workflowValidator.validate(topologyContext, wf); return wf; } private AbstractWorkflowBuilder getWorkflowBuilder(Workflow workflow) { if (workflow.isStandard()) { if (workflow.getName().equals(Workflow.INSTALL_WF)) { return installWorkflowBuilder; } else if (workflow.getName().equals(Workflow.UNINSTALL_WF)) { return uninstallWorkflowBuilder; } } return customWorkflowBuilder; } public Workflow removeStep(Topology topology, String workflowName, String stepId, boolean force) { TopologyContext topologyContext = buildTopologyContext(topology); Workflow wf = topology.getWorkflows().get(workflowName); if (wf == null) { throw new NotFoundException(String.format("The workflow '%s' can not be found", workflowName)); } AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.removeStep(wf, stepId, force); if (log.isDebugEnabled()) { log.debug(WorkflowUtils.debugWorkflow(wf)); } workflowValidator.validate(topologyContext, wf); return wf; } public Workflow renameStep(Topology topology, String workflowName, String stepId, String newStepName) { Workflow wf = topology.getWorkflows().get(workflowName); if (wf == null) { throw new NotFoundException(String.format("The workflow '%s' can not be found", workflowName)); } AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.renameStep(wf, stepId, newStepName); if (log.isDebugEnabled()) { log.debug(WorkflowUtils.debugWorkflow(wf)); } return wf; } public Workflow addActivity(Topology topology, String workflowName, String relatedStepId, boolean before, AbstractActivity activity) { Workflow wf = topology.getWorkflows().get(workflowName); if (wf == null) { throw new NotFoundException(String.format("The workflow '%s' can not be found", workflowName)); } TopologyContext topologyContext = buildTopologyContext(topology); AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.addActivity(wf, relatedStepId, before, activity, topologyContext); WorkflowUtils.fillHostId(wf, topologyContext); if (log.isDebugEnabled()) { log.debug(WorkflowUtils.debugWorkflow(wf)); } workflowValidator.validate(topologyContext, wf); return wf; } public Workflow swapSteps(Topology topology, String workflowName, String stepId, String targetId) { TopologyContext topologyContext = buildTopologyContext(topology); Workflow wf = topology.getWorkflows().get(workflowName); if (wf == null) { throw new NotFoundException(String.format("The workflow '%s' can not be found", workflowName)); } AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.swapSteps(wf, stepId, targetId); WorkflowUtils.fillHostId(wf, topologyContext); if (log.isDebugEnabled()) { log.debug(WorkflowUtils.debugWorkflow(wf)); } workflowValidator.validate(topologyContext, wf); return wf; } public void renameNode(Topology topology, String nodeTemplateName, String newNodeTemplateName) { if (topology.getWorkflows() == null) { return; } TopologyContext topologyContext = buildTopologyContext(topology); for (Workflow wf : topology.getWorkflows().values()) { AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); builder.renameNode(wf, nodeTemplateName, newNodeTemplateName); WorkflowUtils.fillHostId(wf, topologyContext); workflowValidator.validate(topologyContext, wf); } } public Workflow reinitWorkflow(String workflowName, TopologyContext topologyContext) { Workflow wf = topologyContext.getTopology().getWorkflows().get(workflowName); if (wf == null) { throw new NotFoundException(String.format("The workflow '%s' can not be found", workflowName)); } if (!wf.isStandard()) { throw new BadWorkflowOperationException(String.format("Reinit can not be performed on non standard workflow '%s'", workflowName)); } AbstractWorkflowBuilder builder = getWorkflowBuilder(wf); wf = builder.reinit(wf, topologyContext); WorkflowUtils.fillHostId(wf, topologyContext); workflowValidator.validate(topologyContext, wf); return wf; } public interface TopologyContext { Topology getTopology(); <T extends AbstractToscaType> T findElement(Class<T> clazz, String id); } public TopologyContext buildTopologyContext(Topology topology) { return buildCachedTopologyContext(new DefaultTopologyContext(topology)); } public TopologyContext buildCachedTopologyContext(TopologyContext topologyContext) { return new CachedTopologyContext(topologyContext); } private class DefaultTopologyContext implements TopologyContext { private Topology topology; public DefaultTopologyContext(Topology topology) { super(); this.topology = topology; } @Override public Topology getTopology() { return topology; } @Override public <T extends AbstractToscaType> T findElement(Class<T> clazz, String id) { return csarRepoSearchService.getElementInDependencies(clazz, id, topology.getDependencies()); } } private class CachedTopologyContext implements TopologyContext { private TopologyContext wrapped; private Map<Class<? extends AbstractToscaType>, Map<String, AbstractToscaType>> cache = Maps.newHashMap(); public CachedTopologyContext(TopologyContext wrapped) { super(); this.wrapped = wrapped; } @Override public Topology getTopology() { return wrapped.getTopology(); } @SuppressWarnings("unchecked") @Override public <T extends AbstractToscaType> T findElement(Class<T> clazz, String id) { Map<String, AbstractToscaType> typeCache = cache.get(clazz); if (typeCache == null) { if (log.isTraceEnabled()) { log.trace(String.format("TopologyContext type cache not found for type <%s>, init one ...", clazz.getSimpleName())); } typeCache = Maps.newHashMap(); cache.put(clazz, typeCache); } else { if (log.isTraceEnabled()) { log.trace(String.format("TopologyContext type cache found for type <%s>, using it !", clazz.getSimpleName())); } } AbstractToscaType element = typeCache.get(id); if (element == null) { if (log.isTraceEnabled()) { log.trace(String.format("Element not found from cache for type <%s> id <%s>, look for in source ...", clazz.getSimpleName(), id)); } element = wrapped.findElement(clazz, id); typeCache.put(id, element); } else { if (log.isTraceEnabled()) { log.trace(String.format("Element found from cache for type <%s> id <%s>, hit !", clazz.getSimpleName(), id)); } } return (T) element; } } public List<WorkflowTask> validateWorkflows(Topology topology) { List<WorkflowTask> tasks = Lists.newArrayList(); if (topology.getWorkflows() != null) { TopologyContext topologyContext = buildTopologyContext(topology); for (Workflow workflow : topology.getWorkflows().values()) { int errorCount = validateWorkflow(topologyContext, workflow); if (errorCount > 0) { WorkflowTask workflowTask = new WorkflowTask(); workflowTask.setCode(TaskCode.WORKFLOW_INVALID); workflowTask.setWorkflowName(workflow.getName()); workflowTask.setErrorCount(errorCount); tasks.add(workflowTask); } } } return tasks; } /** * Get a workflow from a topoogy or fail with a {@link NotFoundException} * * @param workflowName name of the wrolkflow to retrieve * @param topology {@link Topology } in which to retrieve the workflow * @return The workflow found with the given name */ public Workflow getWorkflow(String workflowName, Topology topology) { Workflow workflow = topology.getWorkflows().get(workflowName); if (workflow == null) { throw new NotFoundException("Workflow <" + workflowName + "> not found in topology <" + topology.getId() + ">"); } return workflow; } }