protected List<String> getJobParents( NodeTemplate nodeTemplate, String nodeName, Map<String, NodeTemplate> nodes) { // Get Chronos parent job dependency String parentJobCapabilityName = "parent_job"; Map<String, NodeTemplate> parentJobs = toscaService.getAssociatedNodesByCapability(nodes, nodeTemplate, parentJobCapabilityName); if (parentJobs.isEmpty()) { return null; } else { // WARNING: cycle check is done later! return Lists.newArrayList(parentJobs.keySet()); } }
protected Job createJob( Map<String, NodeTemplate> nodes, String deploymentId, String nodeName, NodeTemplate nodeTemplate, Map<String, Resource> resources) { try { Job chronosJob = new Job(); // Init job infos // Get the generated UUID for the node (in DB resource ?) // FIXME This is just for prototyping... Otherwise is madness!! Resource resourceJob = resources.get(nodeName); chronosJob.setName(resourceJob.getId()); // TODO Validation chronosJob.setRetries( Integer.parseInt( (String) toscaService.getNodePropertyValueByName(nodeTemplate, "retries").getValue())); chronosJob.setCommand( (String) toscaService.getNodePropertyValueByName(nodeTemplate, "command").getValue()); // TODO Enable epsilon setting in TOSCA tplt ? chronosJob.setEpsilon("PT10S"); ListPropertyValue inputUris = (ListPropertyValue) toscaService.getNodePropertyValueByName(nodeTemplate, "uris"); if (inputUris != null && !inputUris.getValue().isEmpty()) { // Convert List<Object> to List<String> chronosJob.setUris( inputUris .getValue() .stream() .map(e -> ((PropertyValue<?>) e).getValue().toString()) .collect(Collectors.toList())); } List<EnvironmentVariable> envs = new ArrayList<>(); ComplexPropertyValue inputEnvVars = ((ComplexPropertyValue) toscaService.getNodePropertyValueByName(nodeTemplate, "environment_variables")); if (inputEnvVars != null) { for (Map.Entry<String, Object> var : inputEnvVars.getValue().entrySet()) { EnvironmentVariable envVar = new EnvironmentVariable(); envVar.setName(var.getKey()); envVar.setValue(((PropertyValue<?>) var.getValue()).getValue().toString()); envs.add(envVar); } chronosJob.setEnvironmentVariables(envs); } // Docker image // TODO Remove hard-coded? String supportedType = "tosca.artifacts.Deployment.Image.Container.Docker"; DeploymentArtifact image; // <image> artifact available if (nodeTemplate.getArtifacts() == null || (image = nodeTemplate.getArtifacts().get("image")) == null) { throw new IllegalArgumentException( String.format( "<image> artifact not found in node <%s> of type <%s>", nodeName, nodeTemplate.getType())); } // <image> artifact type check if (!image.getArtifactType().equals(supportedType)) { throw new IllegalArgumentException( String.format( "Unsupported artifact type for <image> artifact in node <%s> of type <%s>. " + "Given <%s>, supported <%s>", nodeName, nodeTemplate.getType(), nodeTemplate.getArtifacts().get("image").getArtifactType(), supportedType)); } // Requirements // Get Docker host dependency String dockerCapabilityName = "host"; Map<String, NodeTemplate> dockerRelationships = toscaService.getAssociatedNodesByCapability(nodes, nodeTemplate, dockerCapabilityName); Double dockerNumCpus = null; Double dockerMemSize = null; if (!dockerRelationships.isEmpty()) { /* * WARNING: The TOSCA validation should already check the limits (currently Alien4Cloud does * not...) */ NodeTemplate dockerNode = dockerRelationships.values().iterator().next(); Capability dockerCapability = dockerNode.getCapabilities().get(dockerCapabilityName); dockerNumCpus = Double.parseDouble( (String) toscaService .getCapabilityPropertyValueByName(dockerCapability, "num_cpus") .getValue()); // Converting Memory Size (as TOSCA scalar-unit.size) SizeType tmp = new SizeType(); String memSizeRaw = (String) toscaService .getCapabilityPropertyValueByName(dockerCapability, "mem_size") .getValue(); dockerMemSize = tmp.parse(memSizeRaw).convert("MB"); // Chronos wants MB } Container container = new Container(); container.setType("DOCKER"); // Run the container in Privileged Mode Parameters param = new Parameters(); param.setKey("privileged"); param.setValue("true"); Collection<Parameters> parameters = new ArrayList<Parameters>(); parameters.add(param); container.setParameters(parameters); // FIXME ForcePullImage must be parametrizable by tosca template container.setForcePullImage(true); //////////////////////////////////////////////////////////////// container.setImage( (String) ((PropertyValue<?>) nodeTemplate.getArtifacts().get("image").getFile()).getValue()); if (dockerNumCpus != null) { chronosJob.setCpus(dockerNumCpus); } if (dockerMemSize != null) { chronosJob.setMem(dockerMemSize); } chronosJob.setContainer(container); return chronosJob; } catch (Exception ex) { throw new RuntimeException( String.format( "Failed to parse node <%s> of type <%s>: %s", nodeName, nodeTemplate.getType(), ex.getMessage()), ex); } }
/** * Creates the {@link INDIGOJob} graph based on the given {@link Deployment} (the TOSCA template * is parsed). * * @param deployment the input deployment. * @return the job graph. */ protected Map<String, IndigoJob> generateJobGraph( Deployment deployment, Map<String, OneData> odParameters) { String deploymentId = deployment.getId(); Map<String, IndigoJob> jobs = new HashMap<String, ChronosServiceImpl.IndigoJob>(); // Parse TOSCA template Map<String, NodeTemplate> nodes = null; try { String customizedTemplate = deployment.getTemplate(); /* * FIXME TEMPORARY - Replace hard-coded properties in nodes (WARNING: Cannot be done when * receiving the template because we still miss OneData settings that are obtained during the * WF after the site choice, which in turns depends on the template nodes and properties...) */ customizedTemplate = replaceHardCodedParams(customizedTemplate, odParameters); // Re-parse template (TODO: serialize the template in-memory representation?) ArchiveRoot ar = toscaService.prepareTemplate(customizedTemplate, deployment.getParameters()); nodes = ar.getTopology().getNodeTemplates(); } catch (IOException | ParsingException ex) { throw new OrchestratorException(ex); } // TODO Iterate on Chronos nodes and related dependencies (just ignore others - also if invalid // - for now) // Populate resources (nodes) hashmap to speed up job creation (id-name mapping is needed) Map<String, Resource> resources = deployment .getResources() .stream() .collect(Collectors.toMap(e -> e.getToscaNodeName(), e -> e)); // Only create Indigo Jobs for (Map.Entry<String, NodeTemplate> node : nodes.entrySet()) { NodeTemplate nodeTemplate = node.getValue(); String nodeName = node.getKey(); if (isChronosNode(nodeTemplate)) { Job chronosJob = createJob(nodes, deploymentId, nodeName, nodeTemplate, resources); IndigoJob job = new IndigoJob(nodeName, chronosJob); jobs.put(nodeName, job); } } // Create jobs hierarchy for (Map.Entry<String, IndigoJob> job : jobs.entrySet()) { IndigoJob indigoJob = job.getValue(); String nodeName = job.getKey(); NodeTemplate nodeTemplate = nodes.get(nodeName); // Retrieve Job parents List<String> parentNames = getJobParents(nodeTemplate, nodeName, nodes); if (parentNames != null && !parentNames.isEmpty()) { List<String> chronosParentList = new ArrayList<>(); for (String parentName : parentNames) { IndigoJob parentJob = jobs.get(parentName); // Add this job to the parent parentJob.getChildren().add(indigoJob); // Add the parent to this job indigoJob.getParents().add(parentJob); // Add to the Chronos DSL parent list chronosParentList.add(parentJob.getChronosJob().getName()); } // Update Chronos DSL parent list indigoJob.getChronosJob().setParents(chronosParentList); } } // Validate (no cycles!) // FIXME Shouldn't just return the topological order ? getJobsTopologicalOrder(jobs); return jobs; }