提交 a0df4e27 编写于 作者: S stephenconnolly

adding a demand retention strategy

git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@9436 71c3de6d-444a-0410-be80-ed276b4c234a
上级 52367a4e
......@@ -99,6 +99,14 @@ public abstract class Computer extends AbstractModelObject {
*/
public abstract void launch();
/**
* Do the same as {@link #doDoDisconnect(StaplerRequest, StaplerResponse)} but outside the context
* of serving a request.
*
* If this is the master, no-op
*/
public void disconnect() { }
/**
* Number of {@link Executor}s that are configured for this computer.
*
......
......@@ -6,6 +6,7 @@ import hudson.triggers.SafeTimerTask;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;
/**
* Periodically checks the slaves and try to reconnect dead slaves.
......@@ -24,7 +25,8 @@ public class ComputerRetentionWork extends SafeTimerTask {
// at the moment I don't trust strategies to wait more than 60 minutes
// strategies need to wait at least one minute
final long waitInMins = Math.min(1, Math.max(60, c.getRetentionStrategy().check(c)));
nextCheck.put(c, System.currentTimeMillis() + 60 * 1000 * waitInMins);
nextCheck.put(c, System.currentTimeMillis()
+ TimeUnit.MILLISECONDS.convert(waitInMins, TimeUnit.MINUTES));
}
}
}
......
package hudson.slaves;
import hudson.ExtensionPoint;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.Computer;
import hudson.model.*;
import hudson.util.DescriptorList;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.concurrent.TimeUnit;
/**
* Controls when to take {@link Computer} offline, bring it back online, or even to destroy it.
*
* <p>
* <p/>
* <p/>
* <b>EXPERIMENTAL: SIGNATURE MAY CHANGE IN FUTURE RELEASES</b>
*/
public abstract class RetentionStrategy<T extends Computer> implements Describable<RetentionStrategy<?>>, ExtensionPoint {
......@@ -18,10 +18,8 @@ public abstract class RetentionStrategy<T extends Computer> implements Describab
/**
* This method will be called periodically to allow this strategy to decide what to do with it's owning slave.
*
* @param c
* {@link Computer} for which this strategy is assigned. This object also exposes a bunch of properties
* that the callee can use to decide what action to take.
*
* @param c {@link Computer} for which this strategy is assigned. This object also exposes a bunch of properties
* that the callee can use to decide what action to take.
* @return The number of minutes after which the strategy would like to be checked again. The strategy may be
* rechecked earlier or later that this!
*/
......@@ -54,16 +52,25 @@ public abstract class RetentionStrategy<T extends Computer> implements Describab
* {@link RetentionStrategy} that tries to keep the node online all the time.
*/
public static class Always extends RetentionStrategy<SlaveComputer> {
/**
* Constructs a new Always.
*/
@DataBoundConstructor
public Always() {
}
/**
* {@inheritDoc}
*/
public long check(SlaveComputer c) {
if (c.isOffline() && c.isLaunchSupported())
c.tryReconnect();
return 1;
}
/**
* {@inheritDoc}
*/
public DescriptorImpl getDescriptor() {
return DESCRIPTOR;
}
......@@ -71,10 +78,16 @@ public abstract class RetentionStrategy<T extends Computer> implements Describab
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
private static class DescriptorImpl extends Descriptor<RetentionStrategy<?>> {
/**
* Constructs a new DescriptorImpl.
*/
public DescriptorImpl() {
super(Always.class);
}
/**
* {@inheritDoc}
*/
public String getDisplayName() {
return "Keep this slave on-line as much as possible";
}
......@@ -84,4 +97,125 @@ public abstract class RetentionStrategy<T extends Computer> implements Describab
LIST.add(DESCRIPTOR);
}
}
/**
* {@link hudson.slaves.RetentionStrategy} that tries to keep the node offline when not in use.
*/
public static class Demand extends RetentionStrategy<SlaveComputer> {
/**
* The delay (in minutes) for which the slave must be in demand before tring to launch it.
*/
private final long inDemandDelay;
/**
* The delay (in minutes) for which the slave must be idle before taking it offline.
*/
private final long idleDelay;
private transient Long finishTransition = null;
private transient Boolean finishState = null;
@DataBoundConstructor
public Demand(long inDemandDelay, long idleDelay) {
this.inDemandDelay = inDemandDelay;
this.idleDelay = idleDelay;
}
/**
* Getter for property 'inDemandDelay'.
*
* @return Value for property 'inDemandDelay'.
*/
public long getInDemandDelay() {
return inDemandDelay;
}
/**
* Getter for property 'idleDelay'.
*
* @return Value for property 'idleDelay'.
*/
public long getIdleDelay() {
return idleDelay;
}
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
/**
* {@inheritDoc}
*/
public synchronized long check(SlaveComputer c) {
if (Boolean.valueOf(c.isOffline()).equals(finishState)) {
// reset the timer as we are in the target state!
finishTransition = null;
finishState = !c.isOffline();
}
if (c.isOffline()) {
final Queue queue = Hudson.getInstance().getQueue();
if (queue.getItems().length == 0) {
// reset our timer
finishTransition = null;
} else {
if (finishTransition == null) {
// only just noticed we're in demand
finishTransition = System.currentTimeMillis() +
TimeUnit.MILLISECONDS.convert(inDemandDelay, TimeUnit.MINUTES);
} else if (System.currentTimeMillis() > finishTransition) {
// we've been in demand for long enough
if (c.isOffline() && c.isLaunchSupported())
c.tryReconnect();
finishTransition = null;
}
}
} else {
if (c.isIdle()) {
if (finishTransition == null) {
// only just noticed that we're idle
finishTransition = System.currentTimeMillis() +
TimeUnit.MILLISECONDS.convert(idleDelay, TimeUnit.MINUTES);
} else if (System.currentTimeMillis() > finishTransition) {
// we've been idle for long enough
c.disconnect();
finishTransition = null;
}
} else {
// reset our timer
finishTransition = null;
}
return 1;
}
return 0;
}
/**
* {@inheritDoc}
*/
public Descriptor<RetentionStrategy<?>> getDescriptor() {
return DESCRIPTOR;
}
private static class DescriptorImpl extends Descriptor<RetentionStrategy<?>> {
/**
* Constructs a new DescriptorImpl.
*/
public DescriptorImpl() {
super(Demand.class);
}
/**
* {@inheritDoc}
*/
public String getDisplayName() {
return "Take this slave on-line when in demand and off-line when idle";
}
}
static {
LIST.add(DESCRIPTOR);
}
}
static {
LIST.load(Demand.class);
}
}
......@@ -181,10 +181,15 @@ public final class SlaveComputer extends Computer {
public void doDoDisconnect(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
closeChannel();
disconnect();
rsp.sendRedirect(".");
}
@Override
public void disconnect() {
closeChannel();
}
public void doLaunchSlaveAgent(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
if(channel!=null) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
......
......@@ -91,10 +91,14 @@
selected="${s.retentionStrategy.descriptor==d}"
title="${d.displayName}">
<s:nested>
<j:set var="descriptor" value="${d}"/>
<j:set var="instance"
value="${h.ifThenElse(s.retentionStrategy.descriptor==d,s.retentionStrategy,null)}"/>
<st:include from="${d}" page="${d.configPage}" optional="true"/>
<div name="retentionStrategy">
<table width="100%">
<j:set var="descriptor" value="${d}"/>
<j:set var="instance"
value="${h.ifThenElse(s.retentionStrategy.descriptor==d,s.retentionStrategy,null)}"/>
<st:include from="${d}" page="${d.configPage}" optional="true"/>
</table>
</div>
</s:nested>
</s:dropdownListBlock>
</j:if>
......
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry title="${%Startup demand period}" help="/help/system-config/master-slave/numExecutors.html">
<input class="setting-input number" name="slave.demandPeriod"
type="text" value="${s.demandPeriod}" />
<f:entry title="${%In demand delay}" help="/help/system-config/master-slave/demand/inDemandDelay.html">
<input class="setting-input number validated" name="retentionStrategy.inDemandDelay"
type="text" value="${instance.inDemandDelay}"
checkUrl="'fieldCheck?errorText='+escape('${%In demand delay is mandatory.}')+'&amp;value='+escape(this.value)"/>
</f:entry>
<f:entry title="${%Shutdown idle period}" help="/help/system-config/master-slave/idlePeriod.html">
<input class="setting-input number" name="slave.idlePeriod"
type="text" value="${s.idlePeriod}" />
<f:entry title="${%Idle delay}" help="/help/system-config/master-slave/demand/idleDelay.html">
<input class="setting-input number validated" name="retentionStrategy.idleDelay"
type="text" value="${instance.idleDelay}"
checkUrl="'fieldCheck?errorText='+escape('${%Idle delay is mandatory.}')+'&amp;value='+escape(this.value)"/>
</f:entry>
</j:jelly>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册