From d652291815c2c4d4424e0b704f8c64fc1ad74abe Mon Sep 17 00:00:00 2001 From: kohsuke Date: Sat, 8 Aug 2009 15:29:16 +0000 Subject: [PATCH] [FIXED HUDSON-2431] added OfflineCause class to keep track of why a node is put offline. git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@20558 71c3de6d-444a-0410-be80-ed276b4c234a --- core/src/main/java/hudson/Util.java | 14 ++++ .../main/java/hudson/lifecycle/Lifecycle.java | 14 +--- core/src/main/java/hudson/model/Computer.java | 82 +++++++++++++++--- .../AbstractNodeMonitorDescriptor.java | 14 +++- .../DiskSpaceMonitorDescriptor.java | 18 +++- .../node_monitors/ResponseTimeMonitor.java | 5 +- .../main/java/hudson/slaves/OfflineCause.java | 83 +++++++++++++++++++ .../SimpleScheduledRetentionStrategy.java | 21 ++--- .../java/hudson/slaves/SlaveComputer.java | 26 ++++-- .../hudson/model/Computer/index.jelly | 7 +- .../DiskSpace/cause.jelly | 31 +++++++ .../DiskSpace/cause.properties | 1 + .../ResponseTimeMonitor/Data/cause.jelly | 31 +++++++ .../hudson/slaves/Messages.properties | 2 + .../ChannelTermination/cause.jelly | 27 ++++++ .../SimpleOfflineCause/cause.jelly | 27 ++++++ .../slaves/SlaveComputer/disconnect.jelly | 3 +- .../main/java/hudson/remoting/Channel.java | 2 + 18 files changed, 362 insertions(+), 46 deletions(-) create mode 100644 core/src/main/java/hudson/slaves/OfflineCause.java create mode 100644 core/src/main/resources/hudson/node_monitors/DiskSpaceMonitorDescriptor/DiskSpace/cause.jelly create mode 100644 core/src/main/resources/hudson/node_monitors/DiskSpaceMonitorDescriptor/DiskSpace/cause.properties create mode 100644 core/src/main/resources/hudson/node_monitors/ResponseTimeMonitor/Data/cause.jelly create mode 100644 core/src/main/resources/hudson/slaves/OfflineCause/ChannelTermination/cause.jelly create mode 100644 core/src/main/resources/hudson/slaves/OfflineCause/SimpleOfflineCause/cause.jelly diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index 0c2d721812..67932ee5ba 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -949,6 +949,20 @@ public class Util { } } + /** + * Checks if the public method defined on the base type with the given arguments + * are overridden in the given derived type. + */ + public static boolean isOverridden(Class base, Class derived, String methodName, Class... types) { + // the rewriteHudsonWar method isn't overridden. + try { + return !base.getMethod(methodName, types).equals( + derived.getMethod(methodName,types)); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + public static final FastDateFormat XS_DATETIME_FORMATTER = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'",new SimpleTimeZone(0,"GMT")); // Note: RFC822 dates must not be localized! diff --git a/core/src/main/java/hudson/lifecycle/Lifecycle.java b/core/src/main/java/hudson/lifecycle/Lifecycle.java index bb84e58a0e..9f1ec2087d 100644 --- a/core/src/main/java/hudson/lifecycle/Lifecycle.java +++ b/core/src/main/java/hudson/lifecycle/Lifecycle.java @@ -24,6 +24,7 @@ package hudson.lifecycle; import hudson.ExtensionPoint; +import hudson.Util; import hudson.model.Hudson; import java.io.File; @@ -131,16 +132,6 @@ public abstract class Lifecycle implements ExtensionPoint { return getHudsonWar()!=null; } - private boolean isOverridden(String methodName, Class... types) { - // the rewriteHudsonWar method isn't overridden. - try { - return !getClass().getMethod(methodName, types).equals( - Lifecycle.class.getMethod(methodName,types)); - } catch (NoSuchMethodException e) { - throw new AssertionError(e); - } - } - /** * If this life cycle supports a restart of Hudson, do so. * Otherwise, throw {@link UnsupportedOperationException}, @@ -162,7 +153,8 @@ public abstract class Lifecycle implements ExtensionPoint { * Can the {@link #restart()} method restart Hudson? */ public boolean canRestart() { - return isOverridden("restart"); + // the rewriteHudsonWar method isn't overridden. + return Util.isOverridden(Lifecycle.class,getClass(), "restart"); } private static final Logger LOGGER = Logger.getLogger(Lifecycle.class.getName()); diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 81e5f09122..f031898682 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -25,7 +25,6 @@ package hudson.model; import hudson.EnvVars; import hudson.Util; -import hudson.FilePath; import hudson.model.Descriptor.FormException; import hudson.node_monitors.NodeMonitor; import hudson.remoting.Channel; @@ -38,6 +37,7 @@ import hudson.security.PermissionGroup; import hudson.slaves.ComputerLauncher; import hudson.slaves.RetentionStrategy; import hudson.slaves.WorkspaceList; +import hudson.slaves.OfflineCause; import hudson.tasks.BuildWrapper; import hudson.tasks.Publisher; import hudson.util.DaemonThreadFactory; @@ -47,6 +47,7 @@ import hudson.util.RunList; import hudson.util.Futures; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; @@ -60,9 +61,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Enumeration; -import java.util.Set; -import java.util.HashSet; -import java.util.Collections; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -107,6 +105,11 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces private final CopyOnWriteArrayList oneOffExecutors = new CopyOnWriteArrayList(); private int numExecutors; + + /** + * Contains info about reason behind computer being offline. + */ + protected volatile OfflineCause offlineCause; private long connectTime = 0; @@ -167,6 +170,18 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces return getACL().hasPermission(permission); } + /** + * If the computer was offline (either temporarily or not), + * this method will return the cause. + * + * @return + * null if the system was put offline without given a cause. + */ + @Exported + public OfflineCause getOfflineCause() { + return offlineCause; + } + /** * Gets the channel that can be used to run a program on this computer. * @@ -261,15 +276,38 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces * If this is the master, no-op. This method may return immediately * while the launch operation happens asynchronously. * + * @param cause + * Object that identifies the reason the node was disconnected. + * * @return * {@link Future} to track the asynchronous disconnect operation. * @see #connect(boolean) + * @since 1.320 */ - public Future disconnect() { - connectTime=0; - return Futures.precomputed(null); + public Future disconnect(OfflineCause cause) { + offlineCause = cause; + if (Util.isOverridden(Computer.class,getClass(),"disconnect")) + return disconnect(); // legacy subtypes that extend disconnect(). + + connectTime=0; + return Futures.precomputed(null); } + /** + * Equivalent to {@code disconnect(null)} + * + * @deprecated as of 1.320. + * Use {@link #disconnect(OfflineCause)} and specify the cause. + */ + public Future disconnect() { + if (Util.isOverridden(Computer.class,getClass(),"disconnect",OfflineCause.class)) + // if the subtype already derives disconnect(OfflineCause), delegate to it + return disconnect(null); + + connectTime=0; + return Futures.precomputed(null); + } + /** * Number of {@link Executor}s that are configured for this computer. * @@ -388,7 +426,24 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces return temporarilyOffline; } + /** + * @deprecated as of 1.320. + * Use {@link #setTemporarilyOffline(boolean, OfflineCause)} + */ public void setTemporarilyOffline(boolean temporarilyOffline) { + setTemporarilyOffline(temporarilyOffline,null); + } + + /** + * Marks the computer as temporarily offline.This retains the underlying + * {@link Channel} connection, but prevent builds from executing. + * + * @param cause + * If the first argument is true, specify the reason why the node is being put + * offline. + */ + public void setTemporarilyOffline(boolean temporarilyOffline, OfflineCause cause) { + offlineCause = temporarilyOffline ? cause : null; this.temporarilyOffline = temporarilyOffline; Hudson.getInstance().getQueue().scheduleMaintenance(); } @@ -739,10 +794,17 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces runs.newBuilds(), Run.FEED_ADAPTER, req, rsp ); } - public void doToggleOffline( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { + public void doToggleOffline( StaplerRequest req, StaplerResponse rsp, @QueryParameter String offlineMessage) throws IOException, ServletException { checkPermission(Hudson.ADMINISTER); - - setTemporarilyOffline(!temporarilyOffline); + if(!temporarilyOffline) { + offlineMessage = Util.fixEmptyAndTrim(offlineMessage); + setTemporarilyOffline(!temporarilyOffline, + OfflineCause.create(hudson.slaves.Messages._SlaveComputer_DisconnectedBy( + Hudson.getAuthentication().getName(), + offlineMessage!=null ? " : " + offlineMessage : ""))); + } else { + setTemporarilyOffline(!temporarilyOffline,null); + } rsp.forwardToPreviousPage(req); } diff --git a/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java index d049ee97a7..f685bbb8c0 100644 --- a/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java +++ b/core/src/main/java/hudson/node_monitors/AbstractNodeMonitorDescriptor.java @@ -30,6 +30,7 @@ import hudson.model.ComputerSet; import hudson.model.AdministrativeMonitor; import hudson.triggers.Trigger; import hudson.triggers.SafeTimerTask; +import hudson.slaves.OfflineCause; import java.io.IOException; import java.util.Date; @@ -135,11 +136,10 @@ public abstract class AbstractNodeMonitorDescriptor extends Descriptor extends DescriptorViews + *

+ * {@link OfflineCause} must have cause.jelly that renders a cause + * into HTML. This is used to tell users why the node is put offline. + * This view should render a block element like DIV. + * + * @author Kohsuke Kawaguchi + * @since 1.320 + */ +@ExportedBean +public abstract class OfflineCause { + /** + * {@link OfflineCause} that renders a static text, + * but without any further UI. + */ + public static class SimpleOfflineCause extends OfflineCause { + public final Localizable description; + + private SimpleOfflineCause(Localizable description) { + this.description = description; + } + + @Exported(name="description") + public String toString() { + return description.toString(); + } + } + + public static OfflineCause create(Localizable d) { + if (d==null) return null; + return new SimpleOfflineCause(d); + } + + /** + * Caused by unexpected channel termination. + */ + public static class ChannelTermination extends OfflineCause { + @Exported + public final Exception cause; + + public ChannelTermination(Exception cause) { + this.cause = cause; + } + + public String getShortDescription() { + return cause.toString(); + } + } +} diff --git a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java index 9a5d213e10..0aeb5b916f 100644 --- a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java @@ -40,6 +40,7 @@ import java.util.GregorianCalendar; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; +import static java.util.logging.Level.INFO; /** * {@link RetentionStrategy} that controls the slave based on a schedule. @@ -166,7 +167,7 @@ public class SimpleScheduledRetentionStrategy extends RetentionStrategy disconnect() { - super.disconnect(); + public Future disconnect(OfflineCause cause) { + super.disconnect(cause); return Computer.threadPoolForRemoting.submit(new Runnable() { public void run() { // do this on another thread so that any lengthy disconnect operation diff --git a/core/src/main/resources/hudson/model/Computer/index.jelly b/core/src/main/resources/hudson/model/Computer/index.jelly index 09bb00f5f8..4a7030942c 100644 --- a/core/src/main/resources/hudson/model/Computer/index.jelly +++ b/core/src/main/resources/hudson/model/Computer/index.jelly @@ -29,12 +29,13 @@ THE SOFTWARE.

-
+ +
@@ -48,6 +49,10 @@ THE SOFTWARE. + + + + diff --git a/core/src/main/resources/hudson/node_monitors/DiskSpaceMonitorDescriptor/DiskSpace/cause.jelly b/core/src/main/resources/hudson/node_monitors/DiskSpaceMonitorDescriptor/DiskSpace/cause.jelly new file mode 100644 index 0000000000..e9fe102b85 --- /dev/null +++ b/core/src/main/resources/hudson/node_monitors/DiskSpaceMonitorDescriptor/DiskSpace/cause.jelly @@ -0,0 +1,31 @@ + + + +
+ + ${%blurb(it.gbLeft)} + +
+
\ No newline at end of file diff --git a/core/src/main/resources/hudson/node_monitors/DiskSpaceMonitorDescriptor/DiskSpace/cause.properties b/core/src/main/resources/hudson/node_monitors/DiskSpaceMonitorDescriptor/DiskSpace/cause.properties new file mode 100644 index 0000000000..3320b5136a --- /dev/null +++ b/core/src/main/resources/hudson/node_monitors/DiskSpaceMonitorDescriptor/DiskSpace/cause.properties @@ -0,0 +1 @@ +blurb=Disk space is too low. Only {0}GB left. \ No newline at end of file diff --git a/core/src/main/resources/hudson/node_monitors/ResponseTimeMonitor/Data/cause.jelly b/core/src/main/resources/hudson/node_monitors/ResponseTimeMonitor/Data/cause.jelly new file mode 100644 index 0000000000..c5b6db5a0e --- /dev/null +++ b/core/src/main/resources/hudson/node_monitors/ResponseTimeMonitor/Data/cause.jelly @@ -0,0 +1,31 @@ + + + +
+ + ${%Ping response time is too long or timed out.} + +
+
\ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/Messages.properties b/core/src/main/resources/hudson/slaves/Messages.properties index 70ec20fefc..69c1826a01 100644 --- a/core/src/main/resources/hudson/slaves/Messages.properties +++ b/core/src/main/resources/hudson/slaves/Messages.properties @@ -28,5 +28,7 @@ ComputerLauncher.unexpectedError=Unexpected error in launching a slave. This is ComputerLauncher.abortedLaunch=Launching slave process aborted. CommandLauncher.NoLaunchCommand=No launch command specified DumbSlave.displayName=Dumb Slave +SimpleScheduledRetentionStrategy.FinishedUpTime=Computer has finished its scheduled uptime SimpleScheduledRetentionStrategy.displayName=Take this slave on-line according to a schedule EnvironmentVariablesNodeProperty.displayName=Environment variables +SlaveComputer.DisconnectedBy=Disconnected by {0}{1} diff --git a/core/src/main/resources/hudson/slaves/OfflineCause/ChannelTermination/cause.jelly b/core/src/main/resources/hudson/slaves/OfflineCause/ChannelTermination/cause.jelly new file mode 100644 index 0000000000..34435bdbce --- /dev/null +++ b/core/src/main/resources/hudson/slaves/OfflineCause/ChannelTermination/cause.jelly @@ -0,0 +1,27 @@ + + + +
${h.Throwable(it.exception)}
+
\ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/OfflineCause/SimpleOfflineCause/cause.jelly b/core/src/main/resources/hudson/slaves/OfflineCause/SimpleOfflineCause/cause.jelly new file mode 100644 index 0000000000..964c0474b9 --- /dev/null +++ b/core/src/main/resources/hudson/slaves/OfflineCause/SimpleOfflineCause/cause.jelly @@ -0,0 +1,27 @@ + + + +
${it}
+
\ No newline at end of file diff --git a/core/src/main/resources/hudson/slaves/SlaveComputer/disconnect.jelly b/core/src/main/resources/hudson/slaves/SlaveComputer/disconnect.jelly index a1c2b02fac..d7ab7f72c2 100644 --- a/core/src/main/resources/hudson/slaves/SlaveComputer/disconnect.jelly +++ b/core/src/main/resources/hudson/slaves/SlaveComputer/disconnect.jelly @@ -27,9 +27,10 @@ THE SOFTWARE. -
+ ${%Are you sure about disconnecting?} +
diff --git a/remoting/src/main/java/hudson/remoting/Channel.java b/remoting/src/main/java/hudson/remoting/Channel.java index a68697f234..0763815046 100644 --- a/remoting/src/main/java/hudson/remoting/Channel.java +++ b/remoting/src/main/java/hudson/remoting/Channel.java @@ -355,6 +355,7 @@ public class Channel implements VirtualChannel, IChannel { * @param cause * if the channel is closed abnormally, this parameter * represents an exception that has triggered it. + * Otherwise null. */ public void onClosed(Channel channel, IOException cause) {} } @@ -571,6 +572,7 @@ public class Channel implements VirtualChannel, IChannel { } finally { notifyAll(); + if (e instanceof OrderlyShutdown) e = null; for (Listener l : listeners.toArray(new Listener[listeners.size()])) l.onClosed(this,e); } -- GitLab