Skip to content

Commit b5d1298

Browse files
committed
Clean VM frequency is configurable
1 parent 247cdad commit b5d1298

20 files changed

Lines changed: 39193 additions & 102 deletions

plugin/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
<url>https://github.com/jenkinsci/openstack-cloud-plugin</url>
3636

3737
<properties>
38-
<jenkins.version>2.414.1</jenkins.version>
38+
<jenkins.version>2.419</jenkins.version>
3939
<java.level>11</java.level>
4040
<concurrency>1C</concurrency>
4141
<surefire.useFile>false</surefire.useFile>
@@ -110,7 +110,7 @@
110110
<dependency>
111111
<groupId>org.jenkins-ci.plugins</groupId>
112112
<artifactId>cloud-stats</artifactId>
113-
<version>320.v96b_65297a_4b_b_</version>
113+
<version>336.v788e4055508b_</version>
114114
</dependency>
115115
<dependency>
116116
<groupId>org.jenkins-ci.plugins</groupId>

plugin/src/main/java/jenkins/plugins/openstack/compute/JCloudsCleanupThread.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,15 @@ public JCloudsCleanupThread() {
4848

4949
@Override
5050
public long getRecurrencePeriod() {
51-
return MIN * 10;
51+
// fixed value: 1000 millis
52+
long cleanFreq = 1000;
53+
54+
return cleanFreq;
5255
}
5356

5457
@Override
5558
public void execute(TaskListener listener) {
59+
5660
try {
5761
terminateNodesPendingDeletion();
5862

@@ -61,6 +65,9 @@ public void execute(TaskListener listener) {
6165
terminatesNodesWithoutServers(runningServers);
6266

6367
cleanOrphanedFips();
68+
69+
setCloudLastCleanTime();
70+
6471
} catch (JCloudsCloud.LoginFailure ex) {
6572
LOGGER.log(Level.WARNING, "Unable to authenticate: " + ex.getMessage());
6673
} catch (Throwable ex) {
@@ -70,6 +77,7 @@ public void execute(TaskListener listener) {
7077

7178
private void cleanOrphanedFips() {
7279
for (JCloudsCloud cloud : JCloudsCloud.getClouds()) {
80+
if ((System.currentTimeMillis() - cloud.getLastCleanTime()) < cloud.getCleanfreqToMillis()) continue;
7381
Openstack openstack = cloud.getOpenstack();
7482

7583
List<String> leaked = openstack.getFreeFipIds();
@@ -87,8 +95,17 @@ private void cleanOrphanedFips() {
8795
}
8896
}
8997

98+
private void setCloudLastCleanTime() {
99+
for (JCloudsCloud cloud : JCloudsCloud.getClouds()) {
100+
if ((System.currentTimeMillis() - cloud.getLastCleanTime()) < cloud.getCleanfreqToMillis()) continue;
101+
cloud.setLastCleanTime(System.currentTimeMillis());
102+
}
103+
}
104+
90105
private void terminateNodesPendingDeletion() {
91106
for (final JCloudsComputer comp : JCloudsComputer.getAll()) {
107+
JCloudsCloud cloud = JCloudsCloud.getByName(comp.getId().getCloudName());
108+
if ((System.currentTimeMillis() - cloud.getLastCleanTime()) < cloud.getCleanfreqToMillis()) continue;
92109
if (!comp.isIdle()) continue;
93110

94111
final OfflineCause offlineCause = comp.getNode().getFatalOfflineCause();
@@ -156,6 +173,7 @@ private void deleteComputer(JCloudsComputer comp, CauseOfInterruption coi) {
156173
private @Nonnull HashMap<JCloudsCloud, List<Server>> destroyServersOutOfScope() {
157174
HashMap<JCloudsCloud, List<Server>> runningServers = new HashMap<>();
158175
for (JCloudsCloud jc : JCloudsCloud.getClouds()) {
176+
if ((System.currentTimeMillis() - jc.getLastCleanTime()) < jc.getCleanfreqToMillis()) continue;
159177
runningServers.put(jc, new ArrayList<>());
160178
List<Server> servers = jc.getOpenstack().getRunningNodes();
161179
for (Server server : servers) {
@@ -175,6 +193,9 @@ private void deleteComputer(JCloudsComputer comp, CauseOfInterruption coi) {
175193
private void terminatesNodesWithoutServers(@Nonnull HashMap<JCloudsCloud, List<Server>> runningServers) {
176194
Map<String, JCloudsComputer> jenkinsComputers = new HashMap<>();
177195
for (JCloudsComputer computer: JCloudsComputer.getAll()) {
196+
JCloudsCloud cloud = JCloudsCloud.getByName(computer.getId().getCloudName());
197+
if ((System.currentTimeMillis() - cloud.getLastCleanTime()) < cloud.getCleanfreqToMillis()) continue;
198+
178199
JCloudsSlave node = computer.getNode();
179200
if (node != null) {
180201
jenkinsComputers.put(node.getServerId(), computer);

plugin/src/main/java/jenkins/plugins/openstack/compute/JCloudsCloud.java

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.kohsuke.accmod.restrictions.DoNotUse;
3838
import org.kohsuke.accmod.restrictions.NoExternalUse;
3939
import org.kohsuke.stapler.DataBoundConstructor;
40+
import org.kohsuke.stapler.DataBoundSetter;
4041
import org.kohsuke.stapler.QueryParameter;
4142
import org.kohsuke.stapler.StaplerRequest;
4243
import org.kohsuke.stapler.StaplerResponse;
@@ -51,6 +52,7 @@
5152
import java.io.IOException;
5253
import java.io.PrintWriter;
5354
import java.net.MalformedURLException;
55+
import java.lang.NumberFormatException;
5456
import java.net.URL;
5557
import java.util.ArrayList;
5658
import java.util.Collection;
@@ -85,6 +87,12 @@ public class JCloudsCloud extends Cloud implements SlaveOptions.Holder {
8587

8688
private final @CheckForNull String zone; // OpenStack4j requires null when there is no zone configured
8789

90+
// Clean frequency in seconds. Default 10s
91+
private @Nonnull long cleanfreq = 10;
92+
93+
private long lastCleanTime = System.currentTimeMillis();
94+
95+
8896
// Make sure only diff of defaults is saved so when plugin defaults will change users are not stuck with outdated config
8997
private /*final*/ @Nonnull SlaveOptions slaveOptions;
9098

@@ -128,6 +136,7 @@ public JCloudsCloud(
128136
final @Nonnull String endPointUrl,
129137
final boolean ignoreSsl,
130138
final @CheckForNull String zone,
139+
final long cleanfreq,
131140
final @CheckForNull SlaveOptions slaveOptions,
132141
final @CheckForNull List<JCloudsSlaveTemplate> templates,
133142
final @Nonnull String credentialsId
@@ -138,6 +147,7 @@ public JCloudsCloud(
138147
this.ignoreSsl = TRUE.equals(ignoreSsl);
139148
this.zone = Util.fixEmptyAndTrim(zone);
140149
this.credentialId = credentialsId;
150+
this.cleanfreq = cleanfreq == 0 ? 10 : cleanfreq;
141151
this.slaveOptions = slaveOptions == null ? SlaveOptions.empty() : slaveOptions.eraseDefaults(DescriptorImpl.DEFAULTS);
142152

143153
this.templates = templates == null ? Collections.emptyList() : Collections.unmodifiableList(templates);
@@ -236,6 +246,27 @@ private void injectReferenceIntoTemplates() {
236246
return zone;
237247
}
238248

249+
public void setLastCleanTime(long timeMillis) {
250+
this.lastCleanTime = timeMillis;
251+
}
252+
253+
public long getLastCleanTime() {
254+
return lastCleanTime;
255+
}
256+
257+
public @CheckForNull long getCleanfreq() {
258+
return cleanfreq;
259+
}
260+
261+
public @CheckForNull long getCleanfreqToMillis() {
262+
return cleanfreq * 1000;
263+
}
264+
265+
@DataBoundSetter
266+
public void setCleanfreq(@Nonnull long cleanfreq) {
267+
this.cleanfreq = cleanfreq;
268+
}
269+
239270
/**
240271
* Get a queue of templates to be used to provision slaves of label.
241272
*
@@ -490,7 +521,7 @@ private static void sendPlaintextError(String message, StaplerResponse rsp) thro
490521
if (credential == null) {
491522
throw new LoginFailure("No credentials found for cloud " + name + " (id=" + getCredentialsId() + ")");
492523
}
493-
return Openstack.Factory.get(endPointUrl, ignoreSsl, credential, zone);
524+
return Openstack.Factory.get(endPointUrl, ignoreSsl, credential, zone, cleanfreq);
494525
} catch (AuthenticationException ex) {
495526
throw new LoginFailure(name, ex);
496527
} catch (FormValidation ex) {
@@ -555,13 +586,14 @@ public FormValidation doTestConnection(
555586
@QueryParameter boolean ignoreSsl,
556587
@QueryParameter String credentialsId,
557588
@QueryParameter String endPointUrl,
558-
@QueryParameter String zone
589+
@QueryParameter String zone,
590+
@QueryParameter long cleanfreq
559591
) {
560592
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
561593
try {
562594
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
563595
if (openstackCredential == null) throw FormValidation.error("No credential found for " + credentialsId);
564-
Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
596+
Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
565597
Throwable ex = openstack.sanityCheck();
566598

567599
if (ex != null) {
@@ -589,6 +621,23 @@ public FormValidation doCheckEndPointUrl(@QueryParameter String value) {
589621
return FormValidation.ok();
590622
}
591623

624+
@Restricted(DoNotUse.class)
625+
@RequirePOST
626+
public FormValidation doCheckCleanfreq(@QueryParameter String value) {
627+
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
628+
629+
try {
630+
long parsedCleanFreq = Long.parseLong(value);
631+
if (parsedCleanFreq == 0) {
632+
NumberFormatException nfe = new NumberFormatException("cleanfreq should be strictly greater than 0");
633+
throw nfe;
634+
}
635+
} catch (NumberFormatException ex) {
636+
return FormValidation.error(ex, "Clean frequency must be an integer strictly greater than 0 (>=1)");
637+
}
638+
return FormValidation.ok();
639+
}
640+
592641
@Restricted(DoNotUse.class)
593642
@RequirePOST
594643
public ListBoxModel doFillCredentialsIdItems() {

plugin/src/main/java/jenkins/plugins/openstack/compute/OsAuthDescriptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public final void calcFillSettings(String field, Map<String, Object> attributes)
9494

9595
// Replace direct reference to references to possible relative paths
9696
if (method.getAnnotation(InjectOsAuth.class) != null) {
97-
for (String attr: Arrays.asList("endPointUrl", "ignoreSsl", "credentialsId", "zone")) {
97+
for (String attr: Arrays.asList("endPointUrl", "ignoreSsl", "credentialsId", "zone", "cleanfreq")) {
9898
deps.remove(attr);
9999
for (String offset : getAuthFieldsOffsets()) {
100100
deps.add(offset + "/" + attr);

plugin/src/main/java/jenkins/plugins/openstack/compute/SlaveOptionsDescriptor.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public FormValidation doCheckRetentionTime(
169169
public ListBoxModel doFillFloatingIpPoolItems(
170170
@QueryParameter String floatingIpPool,
171171
@QueryParameter String endPointUrl, @QueryParameter boolean ignoreSsl,
172-
@QueryParameter String credentialsId, @QueryParameter String zone
172+
@QueryParameter String credentialsId, @QueryParameter String zone, @QueryParameter long cleanfreq
173173
) {
174174
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
175175
ListBoxModel m = new ListBoxModel();
@@ -178,7 +178,7 @@ public ListBoxModel doFillFloatingIpPoolItems(
178178
try {
179179
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
180180
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
181-
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
181+
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
182182
for (String p : openstack.getSortedIpPools()) {
183183
m.add(p);
184184
}
@@ -215,7 +215,7 @@ public FormValidation doCheckFloatingIpPool(
215215
public ListBoxModel doFillHardwareIdItems(
216216
@QueryParameter String hardwareId, @QueryParameter String endPointUrl,
217217
@QueryParameter boolean ignoreSsl,
218-
@QueryParameter String credentialsId, @QueryParameter String zone
218+
@QueryParameter String credentialsId, @QueryParameter String zone, @QueryParameter long cleanfreq
219219
) {
220220
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
221221
ListBoxModel m = new ListBoxModel();
@@ -224,7 +224,7 @@ public ListBoxModel doFillHardwareIdItems(
224224
try {
225225
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
226226
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
227-
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
227+
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
228228
for (Flavor flavor : openstack.getSortedFlavors()) {
229229
final String value = flavor.getId();
230230
final String displayText = Openstack.getFlavorInfo(flavor);
@@ -269,7 +269,9 @@ public FormValidation doCheckNetworkId(
269269
@RelativePath("..") @QueryParameter("credentialsId") String credentialsIdCloud,
270270
@RelativePath("../..") @QueryParameter("credentialsId") String credentialsIdTemplate,
271271
@RelativePath("..") @QueryParameter("zone") String zoneCloud,
272-
@RelativePath("../..") @QueryParameter("zone") String zoneTemplate
272+
@RelativePath("../..") @QueryParameter("zone") String zoneTemplate,
273+
@RelativePath("..") @QueryParameter("cleanfreq") long cleanfreqCloud,
274+
@RelativePath("../..") @QueryParameter("cleanfreq") long cleanfreqTemplate
273275
) {
274276
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
275277
if (Util.fixEmpty(value) == null) {
@@ -282,9 +284,10 @@ public FormValidation doCheckNetworkId(
282284
final String credentialsId = getDefault(credentialsIdCloud, credentialsIdTemplate);
283285
final OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
284286
final String zone = getDefault(zoneCloud, zoneTemplate);
287+
final long cleanfreq = cleanfreqCloud + cleanfreqTemplate;
285288
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
286289
try {
287-
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
290+
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
288291
List<String> nids = JCloudsSlaveTemplate.selectNetworkIds(openstack, value);
289292
return FormValidation.ok("Will connect to " + nids.size() + " network(s). Ex.: " + nids);
290293
} catch (IllegalArgumentException | NoSuchElementException ex) {
@@ -371,7 +374,7 @@ public FormValidation doCheckSecurityGroups(
371374
public ComboBoxModel doFillAvailabilityZoneItems(
372375
@QueryParameter String availabilityZone, @QueryParameter String endPointUrl,
373376
@QueryParameter boolean ignoreSsl,
374-
@QueryParameter String credentialsId, @QueryParameter String zone
377+
@QueryParameter String credentialsId, @QueryParameter String zone, @QueryParameter long cleanfreq
375378
) {
376379
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
377380
// Support for availabilityZones is optional in OpenStack, so this is a f:combobox not f:select field.
@@ -380,7 +383,7 @@ public ComboBoxModel doFillAvailabilityZoneItems(
380383
try {
381384
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
382385
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
383-
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
386+
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
384387
for (final AvailabilityZone az : openstack.getAvailabilityZones()) {
385388
final String value = az.getZoneName();
386389
m.add(value);
@@ -406,7 +409,9 @@ public FormValidation doCheckAvailabilityZone(
406409
@RelativePath("..") @QueryParameter("credentialsId") String credentialsIdCloud,
407410
@RelativePath("../..") @QueryParameter("credentialsId") String credentialsIdTemplate,
408411
@RelativePath("..") @QueryParameter("zone") String zoneCloud,
409-
@RelativePath("../..") @QueryParameter("zone") String zoneTemplate
412+
@RelativePath("../..") @QueryParameter("zone") String zoneTemplate,
413+
@RelativePath("..") @QueryParameter("cleanfreq") long cleanfreqCloud,
414+
@RelativePath("../..") @QueryParameter("cleanfreq") long cleanfreqTemplate
410415
) {
411416
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
412417
// Warn user if they've not selected anything AND there's multiple availability zones
@@ -420,9 +425,10 @@ public FormValidation doCheckAvailabilityZone(
420425
final String credentialsId = getDefault(credentialsIdCloud,credentialsIdTemplate);
421426
final OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
422427
final String zone = getDefault(zoneCloud, zoneTemplate);
428+
final long cleanfreq = cleanfreqCloud + cleanfreqTemplate;
423429
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
424430
try {
425-
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
431+
final Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
426432
final int numberOfAZs = openstack.getAvailabilityZones().size();
427433
if (numberOfAZs > 1) {
428434
return FormValidation.warning("Ambiguity warning: Multiple zones found.");
@@ -444,7 +450,7 @@ public ListBoxModel doFillKeyPairNameItems(
444450
@QueryParameter String keyPairName,
445451
@QueryParameter String endPointUrl,
446452
@QueryParameter boolean ignoreSsl,
447-
@QueryParameter String credentialsId, @QueryParameter String zone
453+
@QueryParameter String credentialsId, @QueryParameter String zone, @QueryParameter long cleanfreq
448454
) {
449455
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
450456
ListBoxModel m = new ListBoxModel();
@@ -453,7 +459,7 @@ public ListBoxModel doFillKeyPairNameItems(
453459
try {
454460
OpenstackCredential openstackCredential = OpenstackCredentials.getCredential(credentialsId);
455461
if (haveAuthDetails(endPointUrl, openstackCredential, zone)) {
456-
Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone);
462+
Openstack openstack = Openstack.Factory.get(endPointUrl, ignoreSsl, openstackCredential, zone, cleanfreq);
457463
for (String value : openstack.getSortedKeyPairNames()) {
458464
m.add(value);
459465
}

0 commit comments

Comments
 (0)