diff --git a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/PAGateway.java b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/PAGateway.java index 35567aa26094dbd715fb7cbb8fd8217e891c64bd..729ab1c28603788b4c4474cd420455fd1802a361 100644 --- a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/PAGateway.java +++ b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/PAGateway.java @@ -343,7 +343,8 @@ public class PAGateway { List allNodeCandidates = EntityManagerHelper.createQuery("SELECT nc FROM NodeCandidate nc", NodeCandidate.class).getResultList(); allNodeCandidates.forEach(nodeCandidate -> { - if (JCloudsInstancesUtils.isHandledHardwareInstanceType(nodeCandidate.getCloud().getApi().getProviderName(), + if (nodeCandidate.isByonNodeCandidate() || + JCloudsInstancesUtils.isHandledHardwareInstanceType(nodeCandidate.getCloud().getApi().getProviderName(), nodeCandidate.getHardware().getName())) { if (NodeCandidateUtils.verifyAllFilters(requirements, nodeCandidate)) { filteredNodeCandidates.add(nodeCandidate); @@ -456,6 +457,62 @@ public class PAGateway { TemporaryFilesHelper.delete(fXmlFile); } + /** + * Define a BYON node source + * @param byonNodeList a list of BYON nodes to be connected to the server. + * @param nodeSourceName The name of the node source + */ + public void defineByonNodeSource(List byonNodeList, String nodeSourceName) { + Map variables = new HashMap<>(); + String filename; + String byonIPs = ""; + // Prepare the ip addresses for all the nodes to be added + for(ByonNode byonNode: byonNodeList){ + List tempListIP=byonNode.getIpAddresses(); + assert !tempListIP.isEmpty(); + byonIPs= byonIPs + tempListIP.get(0).getValue() + ","; + } + // Collect the pamr router address and port number + try { + URL endpointPa = (new URL(this.paURL)); + variables.put("rm_host_name", endpointPa.getHost()); + variables.put("pa_port", "" + endpointPa.getPort()); + } catch (MalformedURLException e) { + LOGGER.error(String.valueOf(e.getStackTrace())); + } + /* IMPORTANT: we consider that all of the byon nodes that belong to the same nodesource are going to have the + same info and as a result we will get all the nodesource info from one node, later this should be improved. + TODO */ + assert !byonNodeList.isEmpty(); + ByonNode byonNode = byonNodeList.get(0); + filename = File.separator + "Define_NS_BYON.xml"; + variables.put("NS_name", nodeSourceName); + variables.put("pa_protocol", "http"); + variables.put("tokens", "BYON_"+byonNode.getJob().getJobId()); + variables.put("ssh_username", byonNode.getLoginCredential().getUsername()); + variables.put("ssh_password", byonNode.getLoginCredential().getPassword()); + /* IMPORTANT: Later we should relay only on the ssh_key. For now all the nodes must have the same login + credentials. later by automating the node deployment process we can add the server key automatically. + TODO */ + variables.put("ssh_key", ""); + variables.put("ssh_port", "22"); + variables.put("list_of_ips", byonIPs); + // Create the xml file + File fXmlFile = null; + LOGGER.info("NodeSource deployment workflow filename: " + filename); + try { + fXmlFile = TemporaryFilesHelper.createTempFileFromResource(filename); + } catch (IOException ioe) { + LOGGER.error("Opening the NS deployment workflow file failed due to : " + Arrays.toString(ioe.getStackTrace())); + } + assert fXmlFile != null; + LOGGER.info("Submitting the file: " + fXmlFile.toString()); + LOGGER.info("Trying to deploy the NS: " + nodeSourceName); + JobId jobId = schedulerGateway.submit(fXmlFile, variables); + LOGGER.info("Job submitted with ID: " + jobId); + TemporaryFilesHelper.delete(fXmlFile); + } + /** * Add an EMS deployment to a defined job * @param nodeNames Names of the nodes to which to add EMS deployment @@ -523,6 +580,7 @@ public class PAGateway { Deployment newDeployment = new Deployment(); JSONObject nodeCandidateInfo = node.optJSONObject("nodeCandidateInformation"); newDeployment.setNodeName(node.optString("nodeName")); + newDeployment.setDeploymentType(NodeType.IAAS); newDeployment.setLocationName(nodeCandidateInfo.optString("locationName")); newDeployment.setImageProviderId(nodeCandidateInfo.optString("imageProviderId")); newDeployment.setHardwareProviderId(nodeCandidateInfo.optString("hardwareProviderId")); @@ -562,36 +620,60 @@ public class PAGateway { /** * Register new BYON nodes passed as ByonDefinition object - * @param byonNodeDefinition an object of class ByonDefinition that contains the detials of the nodes to be registered. + * @param byonNodeDefinition objects of class ByonDefinition that contains the detials of the nodes to be registered. * @param jobId A constructed job identifier - * @return ByonNode an object that contains information about the registered Node + * @return ByonNodeList objects of class ByonNode that contains information about the registered Node */ public ByonNode registerNewByonNode(ByonDefinition byonNodeDefinition, String jobId) { Validate.notNull(byonNodeDefinition, "The received Byon node definition is empty. Nothing to be registered."); Validate.notNull(jobId, "The received jobId is empty. Nothing to be registered."); + Job job = EntityManagerHelper.find(Job.class, jobId); + assert job != null; + LOGGER.info("Registering a new BYON definition related to job " + jobId + " ..."); + NodeCandidate byonNC = ByonUtils.createByonNodeCandidate(byonNodeDefinition, jobId); EntityManagerHelper.begin(); - ByonNode newByonNode = new ByonNode(); newByonNode.setName(byonNodeDefinition.getName()); newByonNode.setLoginCredential(byonNodeDefinition.getLoginCredential()); newByonNode.setIpAddresses(byonNodeDefinition.getIpAddresses()); newByonNode.setNodeProperties(byonNodeDefinition.getNodeProperties()); + newByonNode.setJob(job); + newByonNode.setNodeCandidate(byonNC); EntityManagerHelper.persist(newByonNode); EntityManagerHelper.commit(); + LOGGER.info("BYON node registered."); + return newByonNode; - //TODO + /* TODO: + * Avoid duplicate nodes in the database + */ + } + /** * Return the List of registered BYON nodes - * @param jobId A constructed job identifier + * @param jobId A constructed job identifier, If "0" is passed as the JobId all the Byon Nodes will be returned * @return List of ByonNode objects that contains information about the registered Nodes */ public List getByonNodeList(String jobId) { - return null; - //TODO + List filteredByonNodes = new LinkedList<>(); + List listByonNodes = EntityManagerHelper.createQuery("SELECT byon FROM ByonNode byon", ByonNode.class).getResultList(); + if (jobId.equals("0")) { + return listByonNodes; + } else { + for (ByonNode byonNode : listByonNodes) { + if (byonNode.getJob().getJobId().equals(jobId)) { + filteredByonNodes.add(byonNode); + } + } + return filteredByonNodes; + } + /*TODO: + * Add Logging info + */ } /** @@ -601,10 +683,43 @@ public class PAGateway { * @return 0 if nodes has been added properly. A greater than 0 value otherwise. */ public int addByonNodes(Map byonIdPerComponent, String jobId) { + Validate.notNull(byonIdPerComponent, "The received byonIdPerComponent structure is empty. Nothing to be added."); + + EntityManagerHelper.begin(); + byonIdPerComponent.forEach((byonNodeId, componentId) -> { + ByonNode byonNode = EntityManagerHelper.find(ByonNode.class, byonNodeId); + Task task = EntityManagerHelper.find(Task.class, componentId); + + assert byonNode!=null : "The BYON ID passed in the mapping does not exist in the database"; + assert task!=null : "The componentId passed in the mapping does not exist in the database"; + + Deployment newDeployment = new Deployment(); + newDeployment.setNodeName(byonNode.getName()); + newDeployment.setDeploymentType(NodeType.BYON); + newDeployment.setByonNode(byonNode); + + List byonNodeList = new LinkedList<>(); + byonNodeList.add(byonNode); + LOGGER.info("BYON node Added: " + byonNode.getName() + " Ip: " + byonNode.getIpAddresses().get(0).getValue() ); + defineByonNodeSource(byonNodeList, "BYON_NS_"+byonNode.getId()); + LOGGER.info("BYON node source BYON_NS_"+ byonNode.getId() + " is defined"); + + newDeployment.setTask(task); + newDeployment.setNumber(task.getNextDeploymentID()); + EntityManagerHelper.persist(newDeployment); + LOGGER.debug("Deployment created: " + newDeployment.toString()); + + task.addDeployment(newDeployment); + EntityManagerHelper.persist(task); + }); + + EntityManagerHelper.commit(); + + LOGGER.info("BYON nodes added properly."); return 0; - //TODO } + /** * Undeploy clouds * @param cloudIDs List of cloud IDs to remove @@ -700,8 +815,60 @@ public class PAGateway { * @return true if the deletion was done with no errors, false otherwise */ public Boolean deleteByonNode(String byonId) { - return null; - //TODO + EntityManagerHelper.begin(); + ByonNode byonNode = EntityManagerHelper.find(ByonNode.class, byonId); + if (byonNode == null) { + LOGGER.error("The passed BYON ID is not Found in the database"); + return false; + } + else { + if(!undeployByonNs(byonNode, false, true)) { + LOGGER.warn("The BYON node source undeploy finished with errors"); + } + LOGGER.info("Deleting the BYON node " + byonNode.getId() +" from the database" ); + EntityManagerHelper.remove(byonNode); + EntityManagerHelper.commit(); + } + return true; + /*TODO: + * change the hardcoding for preempt and remove variables + * */ + } + + /** + * Undeploy or remove the node source of a BYON node + * @param byonNode an object of class ByonNode to be undeployed or removed. + * @param preempt If true undeploy or remove node source immediately without waiting for nodes to be freed + * @param remove If true completely remove the node source, if false only undeply the node source + * @return true if the resourceManagerGateway return no errors, false otherwise + */ + private Boolean undeployByonNs(ByonNode byonNode, Boolean preempt, Boolean remove) { + assert byonNode!= null : "A null value was passed for byonNode, A node source must have a BYON ID"; + String nodeSourceName = "BYON_NS_" + byonNode.getId(); + if (remove) { + try { + LOGGER.info("Removing BYON node source " + nodeSourceName + " from the ProActive server"); + resourceManagerGateway.removeNodeSource(nodeSourceName, preempt); + } catch (NotConnectedException | PermissionRestException e) { + LOGGER.error(Arrays.toString(e.getStackTrace())); + return false; + } + } + else { + try { + LOGGER.info("Undeploying BYON node source " + nodeSourceName + " from the ProActive server"); + resourceManagerGateway.undeployNodeSource(nodeSourceName, preempt); + } catch (NotConnectedException | PermissionRestException e) { + LOGGER.error(Arrays.toString(e.getStackTrace())); + return false; + } + } + LOGGER.info("BYON node source was removed with no errors"); + return true; + + /*TODO: + * Check if the nodes source is present before calling the resourceManagerGateway + */ } /** @@ -878,15 +1045,26 @@ public class PAGateway { } private ScriptTask createInfraTask(Task task, Deployment deployment, String taskNameSuffix, String nodeToken) { + switch (deployment.getDeploymentType()) { + case IAAS: + return createInfraIAASTask(task, deployment, taskNameSuffix, nodeToken); + case BYON: + return createInfraBYONTask(task, deployment, taskNameSuffix, nodeToken); + } + + return new ScriptTask(); + } + + private ScriptTask createInfraIAASTask(Task task, Deployment deployment, String taskNameSuffix, String nodeToken) { LOGGER.debug("Acquiring node AWS script file: " + getClass().getResource(File.separator + "acquire_node_aws_script.groovy").toString()); ScriptTask deployNodeTask = PAFactory.createGroovyScriptTaskFromFile("acquireAWSNode_" + task.getName() + taskNameSuffix, - "acquire_node_aws_script.groovy"); + "acquire_node_aws_script.groovy"); deployNodeTask.setPreScript(PAFactory.createSimpleScriptFromFIle("pre_acquire_node_script.groovy", "groovy")); Map variablesMap = new HashMap<>(); variablesMap.put("NS_name", new TaskVariable("NS_name", - deployment.getPaCloud().getNodeSourceNamePrefix() + deployment.getLocationName())); + deployment.getPaCloud().getNodeSourceNamePrefix() + deployment.getLocationName())); variablesMap.put("nVMs", new TaskVariable("nVMs", "1", "PA:Integer", false)); variablesMap.put("synchronous", new TaskVariable("synchronous", "true", "PA:Boolean", false)); variablesMap.put("timeout", new TaskVariable("timeout", "700", "PA:Long", false)); @@ -908,24 +1086,57 @@ public class PAGateway { LOGGER.debug("Variables to be added to the task: " + variablesMap.toString()); deployNodeTask.setVariables(variablesMap); + addLocalDefaultNSRegexSelectionScript(deployNodeTask); + + return deployNodeTask; + } + + private ScriptTask createInfraBYONTask(Task task, Deployment deployment, String taskNameSuffix, String nodeToken) { + LOGGER.debug("Acquiring node BYON script file: " + getClass().getResource(File.separator + "acquire_node_BYON_script.groovy").toString()); + ScriptTask deployNodeTask = PAFactory.createGroovyScriptTaskFromFile("acquireBYONNode_" + task.getName() + taskNameSuffix, + "acquire_node_BYON_script.groovy"); + + deployNodeTask.setPreScript(PAFactory.createSimpleScriptFromFIle("pre_acquire_node_script.groovy", "groovy")); + + Map variablesMap = new HashMap<>(); + variablesMap.put("NS_name", new TaskVariable("NS_name", + deployment.getPaCloud().getNodeSourceNamePrefix() + deployment.getLocationName())); + //TODO: To check this. Which ip address is used as host in RM? + variablesMap.put("host_name", new TaskVariable("host_name", + deployment.getByonNode().getIpAddresses().get(0).getValue())); + variablesMap.put("token", new TaskVariable("token", nodeToken)); + + LOGGER.debug("Variables to be added to the task: " + variablesMap.toString()); + deployNodeTask.setVariables(variablesMap); + + addLocalDefaultNSRegexSelectionScript(deployNodeTask); + + return deployNodeTask; + } + + private void addLocalDefaultNSRegexSelectionScript(ScriptTask scriptTask) { try { String selectionScriptFileName = "check_node_source_regexp.groovy"; String[] nodeSourceNameRegex = {"^local$|^Default$"}; SelectionScript selectionScript = new SelectionScript(Utils.getContentWithFileName(selectionScriptFileName), - "groovy", - nodeSourceNameRegex, - true); - deployNodeTask.setSelectionScript(selectionScript); + "groovy", + nodeSourceNameRegex, + true); + scriptTask.setSelectionScript(selectionScript); } catch (InvalidScriptException e) { LOGGER.warn("Selection script could not have been added."); } - - return deployNodeTask; } private ScriptTask createEmsDeploymentTask(EmsDeploymentRequest emsDeploymentRequest, String taskNameSuffix, String nodeToken) { LOGGER.debug("Preparing EMS deployment task"); - ScriptTask emsDeploymentTask = PAFactory.createComplexScriptTaskFromFiles("emsDeployment" + taskNameSuffix,"emsdeploy_mainscript.groovy","groovy","emsdeploy_prescript.sh","bash","emsdeploy_postscript.sh","bash"); + ScriptTask emsDeploymentTask = PAFactory.createComplexScriptTaskFromFiles("emsDeployment" + taskNameSuffix, + "emsdeploy_mainscript.groovy", + "groovy", + "emsdeploy_prescript.sh", + "bash", + "emsdeploy_postscript.sh", + "bash"); Map variablesMap = emsDeploymentRequest.getWorkflowMap(); emsDeploymentTask.addGenericInformation("NODE_ACCESS_TOKEN", nodeToken); emsDeploymentTask.setVariables(variablesMap); @@ -961,7 +1172,7 @@ public class PAGateway { // Let's retrieve the deployment to clone if (optTask.get().getDeployments() == null || optTask.get().getDeployments().isEmpty()) { - LOGGER.error(String.format("No previous deployment found in task [%s] ",taskName)); + LOGGER.error(String.format("No previous deployment found in task [%s] ", taskName)); return 2; } @@ -970,6 +1181,10 @@ public class PAGateway { // Let's clone the deployment/node as needed Deployment oldDeployment = optTask.get().getDeployments().get(0); + if (NodeType.BYON.equals(oldDeployment.getDeploymentType())) { + LOGGER.error(String.format("The previous deployment is a BYON node [%s] ", oldDeployment)); + return 3; + } nodeNames.stream().map(nodeName -> { EmsDeploymentRequest newEmsDeploymentReq = oldDeployment.getEmsDeployment() == null ? null : oldDeployment.getEmsDeployment().clone(nodeName); @@ -982,6 +1197,8 @@ public class PAGateway { oldDeployment.getTask(), false, null, + null, + NodeType.IAAS, null ); }) @@ -1271,10 +1488,24 @@ public class PAGateway { } // For supplied node, I retrieve their deployment - List deployments = nodeNames.stream().map(node -> EntityManagerHelper.find(Deployment.class,node)).filter(Objects::nonNull).collect(Collectors.toList()); + List deployments = nodeNames.stream().map(node -> EntityManagerHelper.find(Deployment.class,node)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + deployments = deployments.stream().filter(deployment -> { + if (NodeType.BYON.equals(deployment.getDeploymentType())) { + LOGGER.warn("Deployment " + deployment.getNodeName() + " is a BYON node and can't be removed in scaling in."); + return false; + } + return true; + }).collect(Collectors.toList()); // For deployed node, I flag their removal - List nodesToBeRemoved = deployments.stream().filter(Deployment::getIsDeployed).map(Deployment::getNodeName).collect(Collectors.toList()); + List nodesToBeRemoved = deployments.stream() + .filter(Deployment::getIsDeployed) + .map(Deployment::getNodeName) + .collect(Collectors.toList()); + LOGGER.info("Nodes to be removed are : " + nodesToBeRemoved); // For every node, I remove the deployment entree deployments.forEach( @@ -1481,13 +1712,7 @@ public class PAGateway { jobToSubmit.getTasks().forEach(task -> { List scriptTasks = buildPATask(task, jobToSubmit); - scriptTasks.forEach(scriptTask -> { - try { - paJob.addTask(scriptTask); - } catch (UserException e) { - LOGGER.error("Task " + task.getName() + " could not be added due to: " + e.toString()); - } - }); + addAllScriptTasksToPAJob(paJob, task, scriptTasks); EntityManagerHelper.persist(task); }); diff --git a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/ByonNode.java b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/ByonNode.java index 17cb40ac4e34678edb7a774f3bf77950ba59c334..89fce2caa5bcb7b2434ca4ce13fcfc4ece986307 100644 --- a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/ByonNode.java +++ b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/ByonNode.java @@ -2,7 +2,9 @@ package org.activeeon.morphemic.model; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import org.hibernate.annotations.GenericGenerator; import javax.persistence.*; @@ -17,6 +19,8 @@ import java.util.Objects; @AllArgsConstructor @NoArgsConstructor @Entity +@Getter +@Setter @Table(name = "BYON_NODE") public class ByonNode implements Serializable { @Id @@ -49,9 +53,8 @@ public class ByonNode implements Serializable { @JsonProperty("diagnostic") private String diagnostic = null; - @Column(name = "NODE_CANDIDATE") - @JsonProperty("nodeCandidate") - private String nodeCandidate = null; + @OneToOne(fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.REFRESH) + private NodeCandidate nodeCandidate = null; @Column(name = "USER_ID") @JsonProperty("userId") @@ -61,6 +64,9 @@ public class ByonNode implements Serializable { @JsonProperty("allocated") private Boolean allocated = null; + @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.REFRESH) + private Job job; + public ByonNode name(String name) { this.name = name; return this; @@ -142,85 +148,24 @@ public class ByonNode implements Serializable { return this; } - /** - * Reason this node was created - * @return reason - **/ - public String getReason() { - return reason; - } - public void setReason(String reason) { - this.reason = reason; - } public ByonNode diagnostic(String diagnostic) { this.diagnostic = diagnostic; return this; } - /** - * Diagnostic information about the node state - * @return diagnostic - **/ - public String getDiagnostic() { - return diagnostic; - } - - public void setDiagnostic(String diagnostic) { - this.diagnostic = diagnostic; - } - - public ByonNode nodeCandidate(String nodeCandidate) { - this.nodeCandidate = nodeCandidate; - return this; - } - - /** - * The node candidate this node was created from if applicable. - * @return nodeCandidate - **/ - public String getNodeCandidate() { - return nodeCandidate; - } - - public void setNodeCandidate(String nodeCandidate) { - this.nodeCandidate = nodeCandidate; - } public ByonNode id(String id) { this.id = id; return this; } - /** - * Unique identifier of this BYON. - * @return id - **/ - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - public ByonNode userId(String userId) { this.userId = userId; return this; } - /** - * User id of the owner of this node. - * @return userId - **/ - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } public ByonNode allocated(Boolean allocated) { this.allocated = allocated; @@ -235,11 +180,6 @@ public class ByonNode implements Serializable { return allocated; } - public void setAllocated(Boolean allocated) { - this.allocated = allocated; - } - - @Override public boolean equals(java.lang.Object o) { if (this == o) { diff --git a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/CloudType.java b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/CloudType.java index 731e19bd87b1961bc3701f2f3e665976929b570c..f9fafdeb7a4e0b28a0cee2b3b308a7c4402cb557 100644 --- a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/CloudType.java +++ b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/CloudType.java @@ -14,6 +14,8 @@ public enum CloudType { PUBLIC("PUBLIC"), + BYON("BYON"), + SIMULATION("SIMULATION"); private String value; diff --git a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/Deployment.java b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/Deployment.java index d19b9f9b1382abefd0efee5cdc16f57f3592dccc..d2fe50eb715394850d3bb94e32078b2e6566df2e 100644 --- a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/Deployment.java +++ b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/Deployment.java @@ -47,18 +47,44 @@ public class Deployment implements Serializable { @Column(name = "NUMBER") private Long number; + @Column(name = "NODE_TYPE") + @Enumerated(EnumType.STRING) + private NodeType deploymentType; + + @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.REFRESH) + private ByonNode byonNode; + @Override public String toString() { - return "Deployment{" + - "nodeName='" + nodeName + '\'' + - ", locationName='" + locationName + '\'' + - ", imageProviderId='" + imageProviderId + '\'' + - ", hardwareProviderId='" + hardwareProviderId + '\'' + - ", isDeployed='" + isDeployed.toString() + '\'' + - ", nodeAccessToken='" + nodeAccessToken + '\'' + - ", number='" + number + '\'' + - ", paCloud='" + paCloud.getNodeSourceNamePrefix() + '\'' + - ", task='" + task.getName() + '\'' + - '}'; + switch (deploymentType) { + case IAAS: + return "Deployment{" + + "nodeName='" + nodeName + '\'' + + ", locationName='" + locationName + '\'' + + ", imageProviderId='" + imageProviderId + '\'' + + ", hardwareProviderId='" + hardwareProviderId + '\'' + + ", isDeployed='" + isDeployed.toString() + '\'' + + ", nodeAccessToken='" + nodeAccessToken + '\'' + + ", number='" + number + '\'' + + ", paCloud='" + paCloud.getNodeSourceNamePrefix() + '\'' + + ", task='" + task.getName() + '\'' + + ", byonNode='" + byonNode + '\'' + + '}'; + case BYON: + return "Deployment{" + + "nodeName='" + nodeName + '\'' + + ", locationName='" + locationName + '\'' + + ", imageProviderId='" + imageProviderId + '\'' + + ", hardwareProviderId='" + hardwareProviderId + '\'' + + ", isDeployed='" + isDeployed.toString() + '\'' + + ", nodeAccessToken='" + nodeAccessToken + '\'' + + ", number='" + number + '\'' + + ", paCloud='" + paCloud + '\'' + + ", task='" + task.getName() + '\'' + + ", byonNode='" + byonNode.getName() + '\'' + + '}'; + default: + return "Deployment{nodeName='" + nodeName + '}'; + } } } diff --git a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/NodeCandidate.java b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/NodeCandidate.java index fd461b176f496b52e22535a3b5e1e97421eadeb2..f0dcfd2a92741d775563c2e25b033146636de27e 100644 --- a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/NodeCandidate.java +++ b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/NodeCandidate.java @@ -71,7 +71,7 @@ public class NodeCandidate implements Serializable { private NodeCandidateTypeEnum nodeCandidateType = null; @Column(name = "JOB_ID_FOR_BYON") - @JsonProperty("nodeType") + @JsonProperty("jobIdForByon") private String jobIdForBYON; @Column(name = "PRICE") @@ -288,6 +288,13 @@ public class NodeCandidate implements Serializable { this.environment = environment; } + /** + * Check if a node candidate is of BYON type + * @return true if yes, false if not + */ + public boolean isByonNodeCandidate() { + return nodeCandidateType.value.equals("BYON"); + } @Override public boolean equals(java.lang.Object o) { diff --git a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/NodeTypeRequirement.java b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/NodeTypeRequirement.java index 4b74245f6c426e68a52e8676aeb9db3d91bc3c1e..a9355cb1faa2e8d03a8ba97047ca30e54347eedb 100644 --- a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/NodeTypeRequirement.java +++ b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/model/NodeTypeRequirement.java @@ -16,7 +16,7 @@ public class NodeTypeRequirement extends Requirement { @JsonProperty("nodeType") private List nodeTypes; - @JsonProperty("nodeType") + @JsonProperty("jobIdForByon") private String jobIdForBYON; /** diff --git a/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/service/ByonUtils.java b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/service/ByonUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..a9f774fcb8d25d30e51d6f131bde4f799503996e --- /dev/null +++ b/scheduling-abstraction-layer/src/main/java/org/activeeon/morphemic/service/ByonUtils.java @@ -0,0 +1,89 @@ +package org.activeeon.morphemic.service; + +import lombok.extern.slf4j.Slf4j; +import org.activeeon.morphemic.model.*; +import org.apache.commons.lang3.RandomStringUtils; + +import java.util.Optional; + +@Slf4j +public class ByonUtils { + + private ByonUtils() {} + + /** + * @param byonDef an Object of class ByonDefinition that contains all the nodes Definition + * @return an object of class NodeCandidate + */ + public static NodeCandidate createByonNodeCandidate(ByonDefinition byonDef, String jobId) { + LOGGER.debug("Creating the BYON node candidate ..."); + //create a dummy cloud + Cloud byonCloud = ByonUtils.getOrCreateDummyByonCloud(); + EntityManagerHelper.begin(); + //set the image + Image image = new Image(); + image.setId("byon-image-" + RandomStringUtils.randomAlphanumeric(16)); + image.setOperatingSystem(byonDef.getNodeProperties().getOperatingSystem()); + + //set the hardware + Hardware hardware = new Hardware(); + hardware.setId("byon-hardware-" + RandomStringUtils.randomAlphanumeric(16)); + hardware.setCores(byonDef.getNodeProperties().getNumberOfCores()); + hardware.setDisk((double) byonDef.getNodeProperties().getDisk()); + hardware.setRam(byonDef.getNodeProperties().getMemory()); + + //set the location + Location location = new Location(); + location.setId("byon-location-" + RandomStringUtils.randomAlphanumeric(16)); + location.setGeoLocation(byonDef.getNodeProperties().getGeoLocation()); + + //define a dummy NC + NodeCandidate byonNC = new NodeCandidate(); + byonNC.setNodeCandidateType(NodeCandidate.NodeCandidateTypeEnum.BYON); + byonNC.setJobIdForBYON(jobId); + byonNC.setPrice(0.0); + byonNC.setMemoryPrice(0.0); + byonNC.setPricePerInvocation(0.0); + byonNC.setCloud(byonCloud); + byonNC.setImage(image); + byonNC.setHardware(hardware); + byonNC.setLocation(location); + + EntityManagerHelper.persist(byonNC); + EntityManagerHelper.commit(); + LOGGER.info("BYON node candidate created."); + return byonNC; + } + + /** + * Create a dummy object of class Cloud to be used for the node candidates + * @return the created byonCloud object + */ + public static Cloud getOrCreateDummyByonCloud() { + LOGGER.debug("Searching for the dummy BYON cloud ..."); + EntityManagerHelper.begin(); + //Check if the Byon cloud already exists + Optional optCloud = Optional.ofNullable(EntityManagerHelper.find(Cloud.class, "byon")); + if (optCloud.isPresent()) { + LOGGER.info("Dummy BYON cloud found!"); + return optCloud.get(); + } + + LOGGER.debug("Creating the dummy BYON cloud ..."); + //else, Byon cloud will be created + Cloud byonCloud = new Cloud(); + byonCloud.setCloudType(CloudType.BYON); + byonCloud.setOwner("BYON"); + byonCloud.setId("byon"); + + //Add the Byon cloud to the database + EntityManagerHelper.persist(byonCloud); + EntityManagerHelper.commit(); + LOGGER.info("Dummy BYON cloud created."); + return byonCloud; + + /* TODO : + * Check if we have to add other variables to the new cloud + */ + } +} diff --git a/scheduling-abstraction-layer/src/main/resources/Define_NS_BYON.xml b/scheduling-abstraction-layer/src/main/resources/Define_NS_BYON.xml new file mode 100644 index 0000000000000000000000000000000000000000..5e471570d2c2d1eb41cb990ec5468cc9b41ba0d4 --- /dev/null +++ b/scheduling-abstraction-layer/src/main/resources/Define_NS_BYON.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 511 + + + 514.5 + + + + + + + + + + + + + + + + ]]> + + + \ No newline at end of file diff --git a/scheduling-abstraction-layer/src/main/resources/acquire_node_BYON_script.groovy b/scheduling-abstraction-layer/src/main/resources/acquire_node_BYON_script.groovy new file mode 100644 index 0000000000000000000000000000000000000000..6591d203f778b058f13276d413060e929611be4b --- /dev/null +++ b/scheduling-abstraction-layer/src/main/resources/acquire_node_BYON_script.groovy @@ -0,0 +1,34 @@ +import org.ow2.proactive.resourcemanager.common.event.RMNodeEvent + +// Conncting to the Scheduler +println "[+] Preparation of Nodes ... " +print "(1/4) Connecting to the RM ..." +rmapi.connect() +println " ... OK !" + +// Getting NS configuration settings +def retCode = 0; +def nodeSourceName = variables.get("NS_name") +def nodeToken = variables.get("token") +def hostName = variables.get("host_name") + +// Enforcing .... +print "(2/4) Searching for nodes ..." +def nodeURLs = rmapi.getRMStateFull().getNodesEvents().findAll { nodeEvent -> + return (nodeSourceName == nodeEvent.getNodeSource() && hostName == nodeEvent.getHostName()); +}.collect { nodeEvent -> + return (nodeEvent.getNodeUrl()); +} +println " ... OK !" +println "Found node URLs : " + nodeURLs.toString() + +print "(3/4) Acquiring nodes ..." +nodeURLs.each { nodeURL -> + rmapi.addNodeToken(nodeURL, nodeToken) +} +println " ... OK !" + +print "(4/4) Logging out ..." +rmapi.disconnect(); +println " ... OK !" +return retCode; \ No newline at end of file