Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@

<developers>
<developer>
<id>olivergondza</id>
<name>Oliver Gondža</name>
<email>[email protected]</email>
<id>olivergondza</id>
<name>Oliver Gondža</name>
<email>[email protected]</email>
</developer>
</developers>

Expand Down Expand Up @@ -233,7 +233,7 @@
</additionalOptions>
</configuration>
</plugin>
</plugins>
</plugins>
</pluginManagement>
<finalName>${project.artifactId}</finalName>
</build>
Expand All @@ -258,3 +258,4 @@
</pluginRepository>
</pluginRepositories>
</project>

Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

import java.lang.Math;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import hudson.model.Executor;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

import hudson.Extension;
import hudson.Functions;
import hudson.model.TaskListener;
import hudson.model.AsyncPeriodicWork;
import org.openstack4j.model.compute.Server;

/**
* Periodically ensure enough slaves are created.
Expand Down Expand Up @@ -92,6 +95,91 @@ public void execute(TaskListener listener) {
}
}

/**
* Methods which return the list of VM
*/
public static List<? extends Server> getVmList(String templateName, String cloudName){
List<? extends Server> vmList = null;
for (JCloudsCloud cloud : JCloudsCloud.getClouds()) {
if (cloud.getDisplayName().equals(cloudName)){
vmList = cloud.getTemplate(templateName).getRunningNodes();
}
}
return vmList;
}

/**
* Methods which return the name of the oldestVM for a specific template.
*/
public static String getOldestVm(String templateName, String cloudName){
return getOldestVM(templateName, cloudName).getName();
}


/**
* Methods which return the oldestVM
* return a Server
* return a VM which is not offline/Pending delete in Jenkins
*/
private static Server getOldestVM(String templateName, String cloudName){
Server oldestVm = null;
long oldestVmTime;
long currentVMTime;
String computerName;
String vmName;
List<? extends Server> vmList = getVmList(templateName, cloudName);
List<JCloudsComputer> listComputer = JCloudsComputer.getAll();
if (oldestVm == null){
oldestVm = vmList.get(0);
}
for (int i = 0; i<vmList.size(); i++){
oldestVmTime = oldestVm.getCreated().getTime();
currentVMTime = vmList.get(i).getCreated().getTime();
if (oldestVmTime > currentVMTime){
for (int y = 0; y<listComputer.size(); y++){
computerName = listComputer.get(y).getName();
vmName = vmList.get(i).getName();
if (computerName.equals(vmName)){
if (!listComputer.get(y).isOffline()){
oldestVm = vmList.get(i);
}
}
}
}
}
return oldestVm;
}


/**
* Methods which return the number of free executors for a specific template
* take the template name in parameter.
*/
public static int getNumOfFreeExec(String templateName) {
int nbFreeExecutors = 0;
//List of Jenkins computer (VM into Jenkins)
List<JCloudsComputer> listComputer = JCloudsComputer.getAll();
List<Executor> listExecutors;
//For each computer..
for (int y=0; y<listComputer.size(); y++) {
//If this Virtual machine was created with the Template in parameters
//and
//If this VM is connected
if ((listComputer.get(y).getId().getTemplateName().equals(templateName)) && (listComputer.get(y).isOnline())){
//We've get the VM executors into a List
listExecutors = listComputer.get(y).getExecutors();
//For each executors in the list
for (int i = 0; i<listExecutors.size(); i++) {
//If the executor is free
if (listExecutors.get(i).isIdle()){
nbFreeExecutors = nbFreeExecutors + 1;
}
}
}
}
return nbFreeExecutors;
}

/**
* Should a slave be retained to meet the minimum instances constraint?
*
Expand All @@ -115,4 +203,33 @@ public void execute(TaskListener listener) {

@Override protected Level getNormalLoggingLevel() { return Level.FINE; }
@Override protected Level getSlowLoggingLevel() { return Level.INFO; }

public static class OldestVM {

//attributes
public String templateName;
public String oldestVmName;

//Constructor
public OldestVM(String templateName, String oldestVmName){
this.templateName = templateName;
this.oldestVmName = oldestVmName;
}

public String getOldestVmName() {
return oldestVmName;
}

public String getTemplateName() {
return templateName;
}

public void setOldestVmName(String oldestVmName) {
this.oldestVmName = oldestVmName;
}

public void setTemplateName(String templateName) {
this.templateName = templateName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
Expand All @@ -20,6 +22,7 @@
*/
public class JCloudsRetentionStrategy extends RetentionStrategy<JCloudsComputer> {
private transient ReentrantLock checkLock;
private static List<JCloudsPreCreationThread.OldestVM> listOldestVm = new ArrayList<>();

@DataBoundConstructor
public JCloudsRetentionStrategy() {
Expand Down Expand Up @@ -48,33 +51,105 @@ public long check(JCloudsComputer c) {
}

private void doCheck(JCloudsComputer c) {
if (c.isPendingDelete()) return; // No need to do it again
if (c.isPendingDelete()) return;
if (c.isConnecting()) return; // Do not discard slave while launching for the first time when "idle time" does not make much sense
if (!c.isIdle() || c.getOfflineCause() instanceof OfflineCause.UserCause) return; // Occupied by user initiated activity

final JCloudsSlave node = c.getNode();
if (node == null) return; // Node is gone already

final int retentionTime = node.getSlaveOptions().getRetentionTime();
if (retentionTime <= 0) return; // 0 is handled in JCloudsComputer, negative values needs no handling
final long idleSince = c.getIdleStart();
final long idleMilliseconds = getNow() - idleSince;
if (idleMilliseconds > TimeUnit.MINUTES.toMillis(retentionTime)) {
if (JCloudsPreCreationThread.isNeededReadyComputer(node.getComputer())) {
LOGGER.info("Keeping " + c .getName() + " to meet minimum requirements");
return;
boolean oldestSlaveTermination = node.getSlaveOptions().getOldestSlaveTermination();

//case where the checkbox isn't checked
if (oldestSlaveTermination == false) {
if (!c.isIdle() || c.getOfflineCause() instanceof OfflineCause.UserCause) return; // Occupied by user initiated activity

if (node == null) return; // Node is gone already

final int retentionTime = node.getSlaveOptions().getRetentionTime();

if (retentionTime <= 0) return; // 0 is handled in JCloudsComputer, negative values needs no handling
final long idleSince = c.getIdleStart();
final long idleMilliseconds = getNow() - idleSince;
if (idleMilliseconds > TimeUnit.MINUTES.toMillis(retentionTime)) {
if (JCloudsPreCreationThread.isNeededReadyComputer(node.getComputer())) {
LOGGER.info("Keeping " + c.getName() + " to meet minimum requirements");
return;
}
LOGGER.info("Scheduling " + c.getName() + " for termination after " + retentionTime + " minutes as it was idle since " + new Date(idleSince));
if (LOGGER.isLoggable(Level.FINE)) {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Jenkins.XSTREAM2.toXMLUTF8(node, out);
LOGGER.fine(out.toString("UTF-8"));
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Failed to dump node config", e);
}
}
c.setPendingDelete(true);
}
LOGGER.info("Scheduling " + c .getName() + " for termination after " + retentionTime+ " minutes as it was idle since " + new Date(idleSince));
if (LOGGER.isLoggable(Level.FINE)) {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Jenkins.XSTREAM2.toXMLUTF8(node, out);
LOGGER.fine(out.toString("UTF-8"));
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Failed to dump node config", e);
}

//case where the checkbox is checked
if ((oldestSlaveTermination == true)) {

String templateName = c.getId().getTemplateName();
String cloudName = c.getId().getCloudName();
String oldestVmName = "";

//number of free executors in our VM template
int numOfFreeExec = JCloudsPreCreationThread.getNumOfFreeExec(templateName);

//number of executor required for our template
int numOfMinExec = c.getNode().getSlaveOptions().getNumExecutors() * node.getSlaveOptions().getInstancesMin();

//If oldest VM list is empty
if (listOldestVm.isEmpty()){
//Find the oldest VM for the template of the current Computer
oldestVmName = JCloudsPreCreationThread.getOldestVm(templateName, cloudName);
//New object
JCloudsPreCreationThread.OldestVM oldestVM = new JCloudsPreCreationThread.OldestVM(templateName, oldestVmName);
//Add the object to the oldest VM list
listOldestVm.add(oldestVM);
} else {

boolean templateInTheList = false;
for (int i = 0; i<listOldestVm.size(); i++){
if (listOldestVm.get(i).getTemplateName().equals(templateName)){
templateInTheList = true;
}
}
//If the template is not in the oldestVMlist...
if (templateInTheList == false){
//find the oldestVM for the template of the current computer
oldestVmName = JCloudsPreCreationThread.getOldestVm(templateName, cloudName);
//new object
JCloudsPreCreationThread.OldestVM oldestVM = new JCloudsPreCreationThread.OldestVM(templateName, oldestVmName);
//Add the object to the oldest VM list
listOldestVm.add(oldestVM);
}
}
c.setPendingDelete(true);


//If the number of free executors is higher than the number of minimum executors required
//and
//If the computer taken in parameter is the oldestVM

if (numOfFreeExec > numOfMinExec){
//If the current VM is the oldest
for (int i = 0; i<listOldestVm.size(); i++){
if (templateName.equals(listOldestVm.get(i).templateName)){
if (listOldestVm.get(i).getOldestVmName().equals(c.getName())){
//We set the oldestVM to "PendingDelete"
c.setPendingDelete(true);
System.out.println(c.getName() + " Schedule for deleting");
//Find the oldestVM for this template
oldestVmName = JCloudsPreCreationThread.getOldestVm(templateName, cloudName);
//Set the new oldest VM in the list
listOldestVm.get(i).setOldestVmName(oldestVmName);
System.out.println("The oldest VM for the template "+templateName+" is now "+oldestVmName);

}
}
}
}

}
}

Expand Down
Loading