diff --git a/pom.xml b/pom.xml
index d31056ac..c5b99e36 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,9 +30,9 @@
- olivergondza
- Oliver Gondža
- ogondza@gmail.com
+ olivergondza
+ Oliver Gondža
+ ogondza@gmail.com
@@ -233,7 +233,7 @@
-
+
${project.artifactId}
@@ -258,3 +258,4 @@
+
diff --git a/src/main/java/jenkins/plugins/openstack/compute/JCloudsPreCreationThread.java b/src/main/java/jenkins/plugins/openstack/compute/JCloudsPreCreationThread.java
index 5dc60f0f..845f1f03 100644
--- a/src/main/java/jenkins/plugins/openstack/compute/JCloudsPreCreationThread.java
+++ b/src/main/java/jenkins/plugins/openstack/compute/JCloudsPreCreationThread.java
@@ -2,10 +2,12 @@
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;
@@ -13,6 +15,7 @@
import hudson.Functions;
import hudson.model.TaskListener;
import hudson.model.AsyncPeriodicWork;
+import org.openstack4j.model.compute.Server;
/**
* Periodically ensure enough slaves are created.
@@ -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 listComputer = JCloudsComputer.getAll();
+ if (oldestVm == null){
+ oldestVm = vmList.get(0);
+ }
+ for (int i = 0; i currentVMTime){
+ for (int y = 0; y listComputer = JCloudsComputer.getAll();
+ List listExecutors;
+ //For each computer..
+ for (int y=0; y {
private transient ReentrantLock checkLock;
+ private static List listOldestVm = new ArrayList<>();
@DataBoundConstructor
public JCloudsRetentionStrategy() {
@@ -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 numOfMinExec){
+ //If the current VM is the oldest
+ for (int i = 0; i, Serializable {
private static final long serialVersionUID = -1L;
- private static final SlaveOptions EMPTY = new SlaveOptions(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
+ private static final SlaveOptions EMPTY = new SlaveOptions(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, false);
// Provisioning attributes
private /*final*/ @CheckForNull BootSource bootSource;
@@ -75,6 +77,7 @@ public class SlaveOptions implements Describable, Serializable {
// Slave attributes
private final Integer retentionTime;
+ private final boolean oldestSlaveTermination;
// Replaced by BootSource
@Deprecated private transient @CheckForNull String imageId;
@@ -143,6 +146,8 @@ public Integer getRetentionTime() {
return retentionTime;
}
+ public boolean getOldestSlaveTermination() { return oldestSlaveTermination; }
+
public SlaveOptions(Builder b) {
this(
b.bootSource,
@@ -160,7 +165,8 @@ public SlaveOptions(Builder b) {
b.jvmOptions,
b.fsRoot,
b.launcherFactory,
- b.retentionTime
+ b.retentionTime,
+ b.oldestSlaveTermination
);
}
@@ -181,7 +187,8 @@ public SlaveOptions(
String jvmOptions,
String fsRoot,
LauncherFactory launcherFactory,
- Integer retentionTime
+ Integer retentionTime,
+ boolean oldestSlaveTermination
) {
this.bootSource = bootSource;
this.hardwareId = Util.fixEmpty(hardwareId);
@@ -199,6 +206,7 @@ public SlaveOptions(
this.fsRoot = Util.fixEmpty(fsRoot);
this.launcherFactory = launcherFactory;
this.retentionTime = retentionTime;
+ this.oldestSlaveTermination = TRUE.equals(oldestSlaveTermination);
}
private Object readResolve() {
@@ -230,6 +238,7 @@ private Object readResolve() {
.fsRoot(_override(this.fsRoot, o.fsRoot))
.launcherFactory(_override(this.launcherFactory, o.launcherFactory))
.retentionTime(_override(this.retentionTime, o.retentionTime))
+ .oldestSlaveTermination(_override(this.oldestSlaveTermination, o.oldestSlaveTermination))
.build()
;
}
@@ -259,6 +268,7 @@ private Object readResolve() {
.fsRoot(_erase(this.fsRoot, defaults.fsRoot))
.launcherFactory(_erase(this.launcherFactory, defaults.launcherFactory))
.retentionTime(_erase(this.retentionTime, defaults.retentionTime))
+ .oldestSlaveTermination(this.oldestSlaveTermination)
.build()
;
}
@@ -289,6 +299,7 @@ public String toString() {
.append("fsRoot", fsRoot)
.append("launcherFactory", launcherFactory)
.append("retentionTime", retentionTime)
+ .append("oldestSlaveTermination", oldestSlaveTermination)
.toString()
;
}
@@ -361,6 +372,7 @@ public Builder getBuilder() {
.fsRoot(fsRoot)
.launcherFactory(launcherFactory)
.retentionTime(retentionTime)
+ .oldestSlaveTermination(oldestSlaveTermination)
;
}
@@ -394,6 +406,7 @@ public static final class Builder {
private @CheckForNull LauncherFactory launcherFactory;
private @CheckForNull Integer retentionTime;
+ private boolean oldestSlaveTermination;
public Builder() {}
@@ -480,6 +493,12 @@ public Builder() {}
this.retentionTime = retentionTime;
return this;
}
+
+ public @Nonnull Builder oldestSlaveTermination(boolean oldestSlaveTermination) {
+ this.oldestSlaveTermination = oldestSlaveTermination;
+ return this;
+ }
+
}
/**
diff --git a/src/main/resources/jenkins/plugins/openstack/compute/SlaveOptions/config.jelly b/src/main/resources/jenkins/plugins/openstack/compute/SlaveOptions/config.jelly
index 82913f40..310b2b7d 100644
--- a/src/main/resources/jenkins/plugins/openstack/compute/SlaveOptions/config.jelly
+++ b/src/main/resources/jenkins/plugins/openstack/compute/SlaveOptions/config.jelly
@@ -63,6 +63,9 @@
+
+
+
diff --git a/src/test/java/jenkins/plugins/openstack/PluginTestRule.java b/src/test/java/jenkins/plugins/openstack/PluginTestRule.java
index 7ac8689e..f0ac0fab 100644
--- a/src/test/java/jenkins/plugins/openstack/PluginTestRule.java
+++ b/src/test/java/jenkins/plugins/openstack/PluginTestRule.java
@@ -114,7 +114,7 @@ public static SlaveOptions dummySlaveOptions() {
}
return new SlaveOptions(
new BootSource.VolumeSnapshot("id"), "hw", "nw1,mw2", "dummyUserDataId", 1, 2, "pool", "sg", "az", 1, null, 10,
- "jvmo", "fsRoot", LauncherFactory.JNLP.JNLP, 1
+ "jvmo", "fsRoot", LauncherFactory.JNLP.JNLP, 1, false
);
}
diff --git a/src/test/java/jenkins/plugins/openstack/compute/JCloudsCloudTest.java b/src/test/java/jenkins/plugins/openstack/compute/JCloudsCloudTest.java
index d3c829b7..9136320e 100644
--- a/src/test/java/jenkins/plugins/openstack/compute/JCloudsCloudTest.java
+++ b/src/test/java/jenkins/plugins/openstack/compute/JCloudsCloudTest.java
@@ -151,10 +151,10 @@ public void presentUIDefaults() throws Exception {
JCloudsSlaveTemplate template = new JCloudsSlaveTemplate("template", "label", new SlaveOptions(
new BootSource.Image("iid"), "hw", "nw", "ud", 1, 0, "public", "sg", "az", 2, "kp", 3, "jvmo", "fsRoot", LauncherFactory.JNLP.JNLP, 4
- ));
+ , false));
JCloudsCloud cloud = new JCloudsCloud("openstack", "endPointUrl", false,"zone", new SlaveOptions(
new BootSource.VolumeSnapshot("vsid"), "HW", "NW", "UD", 6, 4, null, "SG", "AZ", 7, "KP", 8, "JVMO", "FSrOOT", new LauncherFactory.SSH("cid"), 9
- ), Collections.singletonList(template),openstackAuth);
+ , false), Collections.singletonList(template),openstackAuth);
j.jenkins.clouds.add(cloud);
JenkinsRule.WebClient wc = j.createWebClient();
diff --git a/src/test/java/jenkins/plugins/openstack/compute/JCloudsRetentionStrategyTest.java b/src/test/java/jenkins/plugins/openstack/compute/JCloudsRetentionStrategyTest.java
index 35f3ec52..0dd400f9 100644
--- a/src/test/java/jenkins/plugins/openstack/compute/JCloudsRetentionStrategyTest.java
+++ b/src/test/java/jenkins/plugins/openstack/compute/JCloudsRetentionStrategyTest.java
@@ -175,7 +175,8 @@ public void doNotRemoveSlaveShortlyAfterConnection() throws Exception {
Assume.assumeFalse(Functions.isWindows());
LauncherFactory launcherFactory = new TestCommandLauncherFactory("bash -c \"sleep 70 && java -jar '%s'\"");
JCloudsCloud cloud = j.configureSlaveProvisioningWithFloatingIP(j.dummyCloud(j.dummySlaveTemplate(
- j.defaultSlaveOptions().getBuilder().retentionTime(1).launcherFactory(launcherFactory).build(),
+ j.defaultSlaveOptions().getBuilder().retentionTime(1).launcherFactory(launcherFactory)
+ .oldestSlaveTermination(false).build(),
"label"
)));
diff --git a/src/test/java/jenkins/plugins/openstack/compute/SlaveOptionsTest.java b/src/test/java/jenkins/plugins/openstack/compute/SlaveOptionsTest.java
index 310497e5..5c482375 100644
--- a/src/test/java/jenkins/plugins/openstack/compute/SlaveOptionsTest.java
+++ b/src/test/java/jenkins/plugins/openstack/compute/SlaveOptionsTest.java
@@ -90,7 +90,7 @@ public void emptyStrings() {
SlaveOptions nulls = SlaveOptions.empty();
SlaveOptions emptyStrings = new SlaveOptions(
null, "", "", "", null, null, "", "", "", null, "", null, "", "", null, null
- );
+ , false);
SlaveOptions emptyBuilt = SlaveOptions.builder()
.hardwareId("")
.networkId("")