/******************************************************************************* * * Copyright (c) 2004-2009 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi, Stephen Connolly * * *******************************************************************************/ package hudson.slaves; import hudson.ExtensionPoint; import hudson.Util; import hudson.DescriptorExtensionList; import hudson.Extension; import hudson.model.*; import hudson.util.DescriptorList; import org.kohsuke.stapler.DataBoundConstructor; import java.util.logging.Level; import java.util.logging.Logger; /** * Controls when to take {@link Computer} offline, bring it back online, or even * to destroy it. * * @author Stephen Connolly * @author Kohsuke Kawaguchi */ public abstract class RetentionStrategy<T extends Computer> extends AbstractDescribableImpl<RetentionStrategy<?>> implements ExtensionPoint { /** * 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 * computer may be online or offline. 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! */ public abstract long check(T c); /** * This method is called to determine whether manual launching of the slave * is allowed at this point in time. * * @param c {@link Computer} for which this strategy is assigned. This * computer may be online or offline. This object also exposes a bunch of * properties that the callee can use to decide if manual launching is * allowed at this time. * @return {@code true} if manual launching of the slave is allowed at this * point in time. */ public boolean isManualLaunchAllowed(T c) { return true; } /** * Called when a new {@link Computer} object is introduced (such as when * Hudson started, or when a new slave is added.) * * <p> The default implementation of this method delegates to * {@link #check(Computer)}, but this allows {@link RetentionStrategy} to * distinguish the first time invocation from the rest. * * @since 1.275 */ public void start(T c) { check(c); } /** * Returns all the registered {@link RetentionStrategy} descriptors. */ public static DescriptorExtensionList<RetentionStrategy<?>, Descriptor<RetentionStrategy<?>>> all() { return (DescriptorExtensionList) Hudson.getInstance().getDescriptorList(RetentionStrategy.class); } /** * All registered {@link RetentionStrategy} implementations. * * @deprecated as of 1.286 Use {@link #all()} for read access, and * {@link Extension} for registration. */ public static final DescriptorList<RetentionStrategy<?>> LIST = new DescriptorList<RetentionStrategy<?>>((Class) RetentionStrategy.class); /** * Dummy instance that doesn't do any attempt to retention. */ public static final RetentionStrategy<Computer> NOOP = new RetentionStrategy<Computer>() { public long check(Computer c) { return 60; } @Override public void start(Computer c) { c.connect(false); } @Override public Descriptor<RetentionStrategy<?>> getDescriptor() { return DESCRIPTOR; } private final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); class DescriptorImpl extends Descriptor<RetentionStrategy<?>> { public String getDisplayName() { return ""; } } }; /** * Convenient singleton instance, since this {@link RetentionStrategy} is * stateless. */ public static final Always INSTANCE = new Always(); /** * {@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() { } public long check(SlaveComputer c) { if (c.isOffline() && !c.isConnecting() && c.isLaunchSupported()) { c.tryReconnect(); } return 1; } @Extension(ordinal = 100) public static class DescriptorImpl extends Descriptor<RetentionStrategy<?>> { public String getDisplayName() { return Messages.RetentionStrategy_Always_displayName(); } } } /** * {@link hudson.slaves.RetentionStrategy} that tries to keep the node * offline when not in use. */ public static class Demand extends RetentionStrategy<SlaveComputer> { private static final Logger logger = Logger.getLogger(Demand.class.getName()); /** * 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; @DataBoundConstructor public Demand(long inDemandDelay, long idleDelay) { this.inDemandDelay = Math.max(0, inDemandDelay); this.idleDelay = Math.max(1, 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 synchronized long check(SlaveComputer c) { if (c.isOffline()) { final long demandMilliseconds = System.currentTimeMillis() - c.getDemandStartMilliseconds(); if (demandMilliseconds > inDemandDelay * 1000 * 60 /*MINS->MILLIS*/ && c.isLaunchSupported()) { // we've been in demand for long enough logger.log(Level.INFO, "Launching computer {0} as it has been in demand for {1}", new Object[]{c.getName(), Util.getTimeSpanString(demandMilliseconds)}); c.connect(false); } } else if (c.isIdle()) { final long idleMilliseconds = System.currentTimeMillis() - c.getIdleStartMilliseconds(); if (idleMilliseconds > idleDelay * 1000 * 60 /*MINS->MILLIS*/) { // we've been idle for long enough logger.log(Level.INFO, "Disconnecting computer {0} as it has been idle for {1}", new Object[]{c.getName(), Util.getTimeSpanString(idleMilliseconds)}); c.disconnect(OfflineCause.create(Messages._RetentionStrategy_Demand_OfflineIdle())); } } return 1; } @Extension public static class DescriptorImpl extends Descriptor<RetentionStrategy<?>> { public String getDisplayName() { return Messages.RetentionStrategy_Demand_displayName(); } } } }