提交 7723cd7e 编写于 作者: K kohsuke

extension points shouldn't depend on implementation detail class, so promoted...

extension points shouldn't depend on implementation detail class, so promoted ComputerImpl to SlaveComputer.

git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@9402 71c3de6d-444a-0410-be80-ed276b4c234a
上级 995ca238
package hudson;
import hudson.model.Hudson;
import hudson.model.Slave.ComputerImpl;
import hudson.slaves.SlaveComputer;
import hudson.remoting.Channel;
import hudson.remoting.Channel.Listener;
......@@ -159,7 +159,7 @@ public class TcpSlaveAgentListener extends Thread {
}
String nodeName = in.readUTF();
ComputerImpl computer = (ComputerImpl) Hudson.getInstance().getComputer(nodeName);
SlaveComputer computer = (SlaveComputer) Hudson.getInstance().getComputer(nodeName);
if(computer==null) {
error(out, "No such slave: "+nodeName);
return;
......
......@@ -8,38 +8,25 @@ import hudson.slaves.SlaveStartMethod;
import hudson.slaves.SlaveAvailabilityStrategy;
import hudson.slaves.CommandStartMethod;
import hudson.slaves.JNLPStartMethod;
import hudson.maven.agent.Main;
import hudson.maven.agent.PluginManagerInterceptor;
import hudson.slaves.SlaveComputer;
import hudson.model.Descriptor.FormException;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Channel.Listener;
import hudson.remoting.VirtualChannel;
import hudson.remoting.Which;
import hudson.tasks.DynamicLabeler;
import hudson.tasks.LabelFinder;
import hudson.util.ClockDifference;
import hudson.util.NullStream;
import hudson.util.RingBufferLogHandler;
import hudson.util.StreamTaskListener;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
......@@ -262,7 +249,7 @@ public final class Slave implements Node, Serializable {
}
public Computer createComputer() {
return new ComputerImpl(this);
return new SlaveComputer(this);
}
public FilePath getWorkspaceFor(TopLevelItem item) {
......@@ -292,219 +279,6 @@ public final class Slave implements Node, Serializable {
return r.child("workspace");
}
public static final class ComputerImpl extends Computer {
private volatile Channel channel;
private Boolean isUnix;
/**
* Number of failed attempts to reconnect to this node
* (so that if we keep failing to reconnect, we can stop
* trying.)
*/
private transient int numRetryAttempt;
/**
* This is where the log from the remote agent goes.
*/
private File getLogFile() {
return new File(Hudson.getInstance().getRootDir(),"slave-"+nodeName+".log");
}
private ComputerImpl(Slave slave) {
super(slave);
}
public Slave getNode() {
return (Slave)super.getNode();
}
@Override
@Deprecated
public boolean isJnlpAgent() {
return getNode().getStartMethod() instanceof JNLPStartMethod;
}
@Override
public boolean isLaunchSupported() {
return getNode().getStartMethod().isLaunchSupported();
}
/**
* Launches a remote agent asynchronously.
*/
private void launch(final Slave slave) {
closeChannel();
Computer.threadPoolForRemoting.execute(new Runnable() {
public void run() {
// do this on another thread so that the lengthy launch operation
// (which is typical) won't block UI thread.
slave.startMethod.launch(ComputerImpl.this, new StreamTaskListener(openLogFile()));
}
});
}
public OutputStream openLogFile() {
OutputStream os;
try {
os = new FileOutputStream(getLogFile());
} catch (FileNotFoundException e) {
logger.log(Level.SEVERE, "Failed to create log file "+getLogFile(),e);
os = new NullStream();
}
return os;
}
private final Object channelLock = new Object();
/**
* Creates a {@link Channel} from the given stream and sets that to this slave.
*/
public void setChannel(InputStream in, OutputStream out, OutputStream launchLog, Listener listener) throws IOException, InterruptedException {
synchronized(channelLock) {
if(this.channel!=null)
throw new IllegalStateException("Already connected");
Channel channel = new Channel(nodeName,threadPoolForRemoting, Channel.Mode.NEGOTIATE,
in,out, launchLog);
channel.addListener(new Listener() {
public void onClosed(Channel c,IOException cause) {
ComputerImpl.this.channel = null;
}
});
channel.addListener(listener);
PrintWriter log = new PrintWriter(launchLog,true);
{// send jars that we need for our operations
// TODO: maybe I should generalize this kind of "post initialization" processing
FilePath dst = new FilePath(channel,getNode().getRemoteFS());
new FilePath(Which.jarFile(Main.class)).copyTo(dst.child("maven-agent.jar"));
log.println("Copied maven-agent.jar");
new FilePath(Which.jarFile(PluginManagerInterceptor.class)).copyTo(dst.child("maven-interceptor.jar"));
log.println("Copied maven-interceptor.jar");
}
isUnix = channel.call(new DetectOS());
log.println(isUnix?Messages.Slave_UnixSlave():Messages.Slave_WindowsSlave());
// install log handler
channel.call(new LogInstaller());
numRetryAttempt = 0;
// prevent others from seeing a channel that's not properly initialized yet
this.channel = channel;
}
Hudson.getInstance().getQueue().scheduleMaintenance();
}
@Override
public VirtualChannel getChannel() {
return channel;
}
public List<LogRecord> getLogRecords() throws IOException, InterruptedException {
if(channel==null)
return Collections.emptyList();
else
return channel.call(new Callable<List<LogRecord>,RuntimeException>() {
public List<LogRecord> call() {
return new ArrayList<LogRecord>(SLAVE_LOG_HANDLER.getView());
}
});
}
public void doDoDisconnect(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
closeChannel();
rsp.sendRedirect(".");
}
public void doLaunchSlaveAgent(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
if(channel!=null) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
launch();
// TODO: would be nice to redirect the user to "launching..." wait page,
// then spend a few seconds there and poll for the completion periodically.
rsp.sendRedirect("log");
}
public void tryReconnect() {
numRetryAttempt++;
if(numRetryAttempt<6 || (numRetryAttempt%12)==0) {
// initially retry several times quickly, and after that, do it infrequently.
logger.info("Attempting to reconnect "+nodeName);
launch();
}
}
public void launch() {
if(channel==null)
launch(getNode());
}
/**
* Gets the string representation of the slave log.
*/
public String getLog() throws IOException {
return Util.loadFile(getLogFile());
}
/**
* Handles incremental log.
*/
public void doProgressiveLog( StaplerRequest req, StaplerResponse rsp) throws IOException {
new LargeText(getLogFile(),false).doProgressText(req,rsp);
}
/**
* Serves jar files for JNLP slave agents.
*/
public JnlpJar getJnlpJars(String fileName) {
return new JnlpJar(fileName);
}
@Override
protected void kill() {
super.kill();
closeChannel();
}
/**
* If still connected, disconnect.
*/
private void closeChannel() {
Channel c = channel;
channel = null;
isUnix=null;
if(c!=null)
try {
c.close();
} catch (IOException e) {
logger.log(Level.SEVERE, "Failed to terminate channel to "+getDisplayName(),e);
}
}
@Override
protected void setNode(Node node) {
super.setNode(node);
if(channel==null)
// maybe the configuration was changed to relaunch the slave, so try it now.
launch((Slave)node);
}
private static final Logger logger = Logger.getLogger(ComputerImpl.class.getName());
private static final class DetectOS implements Callable<Boolean,IOException> {
public Boolean call() throws IOException {
return File.pathSeparatorChar==':';
}
}
}
/**
* Web-bound object used to serve jar files for JNLP.
*/
......@@ -531,15 +305,15 @@ public final class Slave implements Node, Serializable {
}
public Launcher createLauncher(TaskListener listener) {
ComputerImpl c = getComputer();
SlaveComputer c = getComputer();
return new RemoteLauncher(listener, c.getChannel(), c.isUnix);
}
/**
* Gets the corresponding computer object.
*/
public ComputerImpl getComputer() {
return (ComputerImpl)Hudson.getInstance().getComputer(this);
public SlaveComputer getComputer() {
return (SlaveComputer)Hudson.getInstance().getComputer(this);
}
public Computer toComputer() {
......@@ -576,22 +350,6 @@ public final class Slave implements Node, Serializable {
return this;
}
/**
* This field is used on each slave node to record log records on the slave.
*/
private static final RingBufferLogHandler SLAVE_LOG_HANDLER = new RingBufferLogHandler();
private static class LogInstaller implements Callable<Void,RuntimeException> {
public Void call() {
// avoid double installation of the handler
Logger logger = Logger.getLogger("hudson");
logger.removeHandler(SLAVE_LOG_HANDLER);
logger.addHandler(SLAVE_LOG_HANDLER);
return null;
}
private static final long serialVersionUID = 1L;
}
//
// backwrad compatibility
//
......
......@@ -2,7 +2,6 @@ package hudson.slaves;
import org.kohsuke.stapler.DataBoundConstructor;
import hudson.model.Descriptor;
import hudson.model.Slave;
import hudson.model.Messages;
import hudson.util.StreamTaskListener;
import hudson.util.ProcessTreeKiller;
......@@ -56,7 +55,7 @@ public class CommandStartMethod extends SlaveStartMethod {
return String.format("[%1$tD %1$tT]", new Date());
}
public void launch(Slave.ComputerImpl computer, final StreamTaskListener listener) {
public void launch(SlaveComputer computer, final StreamTaskListener listener) {
try {
listener.getLogger().println(Messages.Slave_Launching(getTimestamp()));
listener.getLogger().println("$ " + getCommand());
......
package hudson.slaves;
import hudson.model.Descriptor;
import hudson.model.Slave;
import hudson.util.StreamTaskListener;
import org.kohsuke.stapler.DataBoundConstructor;
......@@ -21,7 +20,7 @@ public class JNLPStartMethod extends SlaveStartMethod {
return false;
}
public void launch(Slave.ComputerImpl computer, StreamTaskListener listener) {
public void launch(SlaveComputer computer, StreamTaskListener listener) {
// do nothing as we cannot self start
}
......
package hudson.slaves;
import hudson.util.DescriptorList;
import hudson.ExtensionPoint;
import hudson.model.Describable;
import hudson.model.Slave;
import hudson.model.Descriptor;
import org.kohsuke.stapler.StaplerRequest;
import net.sf.json.JSONObject;
import hudson.model.Slave;
import hudson.util.DescriptorList;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* Slave availability strategy
......@@ -22,12 +21,7 @@ public abstract class SlaveAvailabilityStrategy implements Describable<SlaveAvai
* @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 long check(Slave slave, State state) {
Slave.ComputerImpl c = slave.getComputer();
if (c != null && c.isOffline() && c.isLaunchSupported())
c.tryReconnect();
return 5;
}
public abstract long check(Slave slave, State state);
/**
* All registered {@link SlaveAvailabilityStrategy} implementations.
......@@ -36,7 +30,7 @@ public abstract class SlaveAvailabilityStrategy implements Describable<SlaveAvai
Always.DESCRIPTOR
);
public static class State {
public static final class State {
private final boolean jobWaiting;
private final boolean jobRunning;
......@@ -54,14 +48,26 @@ public abstract class SlaveAvailabilityStrategy implements Describable<SlaveAvai
}
}
/**
* {@link SlaveAvailabilityStrategy} that tries to keep the node online all the time.
*/
public static class Always extends SlaveAvailabilityStrategy {
@DataBoundConstructor
public Always() {
}
public long check(Slave slave, State state) {
SlaveComputer c = slave.getComputer();
if (c != null && c.isOffline() && c.isLaunchSupported())
c.tryReconnect();
return 5;
}
public Descriptor<SlaveAvailabilityStrategy> getDescriptor() {
return DESCRIPTOR;
}
public static final Descriptor<SlaveAvailabilityStrategy> DESCRIPTOR =
new DescriptorImpl();
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
private static class DescriptorImpl extends Descriptor<SlaveAvailabilityStrategy> {
public DescriptorImpl() {
......@@ -71,12 +77,6 @@ public abstract class SlaveAvailabilityStrategy implements Describable<SlaveAvai
public String getDisplayName() {
return "Keep this slave on-line as much as possible";
}
public SlaveAvailabilityStrategy newInstance(StaplerRequest req, JSONObject formData) throws FormException {
return new Always();
}
}
}
}
package hudson.slaves;
import hudson.model.Computer;
import hudson.model.Hudson;
import hudson.model.Slave;
import hudson.model.Messages;
import hudson.model.LargeText;
import hudson.model.Node;
import hudson.remoting.Channel;
import hudson.remoting.Which;
import hudson.remoting.VirtualChannel;
import hudson.remoting.Callable;
import hudson.util.StreamTaskListener;
import hudson.util.NullStream;
import hudson.util.RingBufferLogHandler;
import hudson.FilePath;
import hudson.Util;
import hudson.maven.agent.Main;
import hudson.maven.agent.PluginManagerInterceptor;
import java.io.File;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
/**
* {@link Computer} for {@link Slave}s.
*
* @author Kohsuke Kawaguchi
*/
public final class SlaveComputer extends Computer {
private volatile Channel channel;
private boolean isUnix;
/**
* Number of failed attempts to reconnect to this node
* (so that if we keep failing to reconnect, we can stop
* trying.)
*/
private transient int numRetryAttempt;
/**
* This is where the log from the remote agent goes.
*/
private File getLogFile() {
return new File(Hudson.getInstance().getRootDir(),"slave-"+nodeName+".log");
}
public SlaveComputer(Slave slave) {
super(slave);
}
/**
* True if this computer is a Unix machine (as opposed to Windows machine).
*/
public boolean isUnix() {
return isUnix;
}
public Slave getNode() {
return (Slave)super.getNode();
}
@Override
@Deprecated
public boolean isJnlpAgent() {
return getNode().getStartMethod() instanceof JNLPStartMethod;
}
@Override
public boolean isLaunchSupported() {
return getNode().getStartMethod().isLaunchSupported();
}
/**
* Launches a remote agent asynchronously.
*/
private void launch(final Slave slave) {
closeChannel();
Computer.threadPoolForRemoting.execute(new Runnable() {
public void run() {
// do this on another thread so that the lengthy launch operation
// (which is typical) won't block UI thread.
slave.getStartMethod().launch(SlaveComputer.this, new StreamTaskListener(openLogFile()));
}
});
}
public OutputStream openLogFile() {
OutputStream os;
try {
os = new FileOutputStream(getLogFile());
} catch (FileNotFoundException e) {
logger.log(Level.SEVERE, "Failed to create log file "+getLogFile(),e);
os = new NullStream();
}
return os;
}
private final Object channelLock = new Object();
/**
* Creates a {@link Channel} from the given stream and sets that to this slave.
*/
public void setChannel(InputStream in, OutputStream out, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException {
synchronized(channelLock) {
if(this.channel!=null)
throw new IllegalStateException("Already connected");
Channel channel = new Channel(nodeName,threadPoolForRemoting, Channel.Mode.NEGOTIATE,
in,out, launchLog);
channel.addListener(new Channel.Listener() {
public void onClosed(Channel c,IOException cause) {
SlaveComputer.this.channel = null;
}
});
channel.addListener(listener);
PrintWriter log = new PrintWriter(launchLog,true);
{// send jars that we need for our operations
// TODO: maybe I should generalize this kind of "post initialization" processing
FilePath dst = new FilePath(channel,getNode().getRemoteFS());
new FilePath(Which.jarFile(Main.class)).copyTo(dst.child("maven-agent.jar"));
log.println("Copied maven-agent.jar");
new FilePath(Which.jarFile(PluginManagerInterceptor.class)).copyTo(dst.child("maven-interceptor.jar"));
log.println("Copied maven-interceptor.jar");
}
isUnix = channel.call(new DetectOS());
log.println(isUnix? Messages.Slave_UnixSlave():Messages.Slave_WindowsSlave());
// install log handler
channel.call(new LogInstaller());
numRetryAttempt = 0;
// prevent others from seeing a channel that's not properly initialized yet
this.channel = channel;
}
Hudson.getInstance().getQueue().scheduleMaintenance();
}
@Override
public VirtualChannel getChannel() {
return channel;
}
public List<LogRecord> getLogRecords() throws IOException, InterruptedException {
if(channel==null)
return Collections.emptyList();
else
return channel.call(new Callable<List<LogRecord>,RuntimeException>() {
public List<LogRecord> call() {
return new ArrayList<LogRecord>(SLAVE_LOG_HANDLER.getView());
}
});
}
public void doDoDisconnect(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
closeChannel();
rsp.sendRedirect(".");
}
public void doLaunchSlaveAgent(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
if(channel!=null) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
launch();
// TODO: would be nice to redirect the user to "launching..." wait page,
// then spend a few seconds there and poll for the completion periodically.
rsp.sendRedirect("log");
}
public void tryReconnect() {
numRetryAttempt++;
if(numRetryAttempt<6 || (numRetryAttempt%12)==0) {
// initially retry several times quickly, and after that, do it infrequently.
logger.info("Attempting to reconnect "+nodeName);
launch();
}
}
public void launch() {
if(channel==null)
launch(getNode());
}
/**
* Gets the string representation of the slave log.
*/
public String getLog() throws IOException {
return Util.loadFile(getLogFile());
}
/**
* Handles incremental log.
*/
public void doProgressiveLog( StaplerRequest req, StaplerResponse rsp) throws IOException {
new LargeText(getLogFile(),false).doProgressText(req,rsp);
}
/**
* Serves jar files for JNLP slave agents.
*/
public Slave.JnlpJar getJnlpJars(String fileName) {
return new Slave.JnlpJar(fileName);
}
@Override
protected void kill() {
super.kill();
closeChannel();
}
/**
* If still connected, disconnect.
*/
private void closeChannel() {
Channel c = channel;
channel = null;
isUnix=null;
if(c!=null)
try {
c.close();
} catch (IOException e) {
logger.log(Level.SEVERE, "Failed to terminate channel to "+getDisplayName(),e);
}
}
@Override
protected void setNode(Node node) {
super.setNode(node);
if(channel==null)
// maybe the configuration was changed to relaunch the slave, so try it now.
launch((Slave)node);
}
private static final Logger logger = Logger.getLogger(SlaveComputer.class.getName());
private static final class DetectOS implements Callable<Boolean,IOException> {
public Boolean call() throws IOException {
return File.pathSeparatorChar==':';
}
}
/**
* This field is used on each slave node to record log records on the slave.
*/
private static final RingBufferLogHandler SLAVE_LOG_HANDLER = new RingBufferLogHandler();
private static class LogInstaller implements Callable<Void,RuntimeException> {
public Void call() {
// avoid double installation of the handler
Logger logger = Logger.getLogger("hudson");
logger.removeHandler(SLAVE_LOG_HANDLER);
logger.addHandler(SLAVE_LOG_HANDLER);
return null;
}
private static final long serialVersionUID = 1L;
}
}
package hudson.slaves;
import hudson.model.Computer;
import hudson.model.Hudson;
import hudson.model.Queue;
import hudson.model.Slave;
import hudson.triggers.SafeTimerTask;
import java.util.Map;
......@@ -15,23 +15,23 @@ import java.util.WeakHashMap;
*/
public class SlaveReconnectionWork extends SafeTimerTask {
/**
* Use weak hash map to avoid leaking {@link Slave}.
* Use weak hash map to avoid leaking {@link Computer}.
*/
private final Map<Slave, Long> nextCheck = new WeakHashMap<Slave, Long>();
private final Map<Computer,Long> nextCheck = new WeakHashMap<Computer,Long>();
protected void doRun() {
final Queue queue = Hudson.getInstance().getQueue();
for (Slave s : Hudson.getInstance().getSlaves()) {
if (!nextCheck.containsKey(s) || System.currentTimeMillis() > nextCheck.get(s)) {
boolean hasJob = !s.getComputer().isIdle();
for (Computer c : Hudson.getInstance().getComputers()) {
if (!nextCheck.containsKey(c) || System.currentTimeMillis() > nextCheck.get(c)) {
boolean hasJob = !c.isIdle();
// TODO get only the items from the queue that can apply to this slave
SlaveAvailabilityStrategy.State state = new SlaveAvailabilityStrategy.State(queue.getItems().length > 0, hasJob);
// 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, s.getAvailabilityStrategy().check(s, state)));
nextCheck.put(s, System.currentTimeMillis() + 60 * 1000 * waitInMins);
final long waitInMins = Math.min(1, Math.max(60, c.getAvailabilityStrategy().check(c, state)));
nextCheck.put(c, System.currentTimeMillis() + 60 * 1000 * waitInMins);
}
}
}
......
package hudson.slaves;
import hudson.ExtensionPoint;
import hudson.model.Slave.ComputerImpl;
import hudson.slaves.SlaveComputer;
import hudson.model.Describable;
import hudson.model.Computer;
import hudson.remoting.Channel.Listener;
......@@ -30,7 +30,7 @@ public abstract class SlaveStartMethod implements Describable<SlaveStartMethod>,
* Launches the slave agent for the given {@link Computer}.
*
* <p>
* If the slave agent is launched successfully, {@link ComputerImpl#setChannel(InputStream, OutputStream, OutputStream, Listener)}
* If the slave agent is launched successfully, {@link SlaveComputer#setChannel(InputStream, OutputStream, OutputStream, Listener)}
* should be invoked in the end to notify Hudson of the established connection.
* The operation could also fail, in which case there's no need to make any callback notification,
* (except to notify the user of the failure through {@link StreamTaskListener}.)
......@@ -38,7 +38,7 @@ public abstract class SlaveStartMethod implements Describable<SlaveStartMethod>,
* @param listener
* The progress of the launch, as well as any error, should be sent to this listener.
*/
public abstract void launch(ComputerImpl computer, StreamTaskListener listener);
public abstract void launch(SlaveComputer computer, StreamTaskListener listener);
/**
* All registered {@link SlaveStartMethod} implementations.
......
<!-- Disconnect confirmation -->
<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" xmlns:i="jelly:fmt">
<l:layout title="${it.displayName} log">
<st:include page="sidepanel.jelly" />
<l:main-panel>
<form method="get" action="doDisconnect">
Are you sure about disconnecting?
<f:submit value="Yes" />
</form>
</l:main-panel>
</l:layout>
</j:jelly>
<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" xmlns:i="jelly:fmt">
<l:layout title="${it.displayName} log" secured="true">
<st:include page="sidepanel.jelly" />
<l:main-panel>
<l:isAdmin>
<pre id="out"></pre>
<div id="spinner">
<img src="${imagesURL}/spinner.gif" />
</div>
<t:progressiveText href="progressiveLog" idref="out" spinner="spinner" />
</l:isAdmin>
</l:main-panel>
</l:layout>
</j:jelly>
<!--
Side panel for a slave.
-->
<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" xmlns:i="jelly:fmt">
<l:header title="${it.displayName}" />
<l:side-panel>
<l:tasks>
<l:task icon="images/24x24/up.gif" href=".." title="${%Back to List}" />
<l:task icon="images/24x24/search.gif" href="${rootURL}/computer/${it.displayName}/" title="${%Status}" />
<l:task icon="images/24x24/notepad.gif" href="${rootURL}/${it.url}/builds" title="${%Build History}" />
<l:isAdmin>
<l:task icon="images/24x24/clipboard.gif" href="log" title="${%Log}" />
<l:task icon="images/24x24/computer.gif" href="systemInfo" title="${%System Information}" />
<j:if test="${it.channel!=null}">
<l:task icon="images/24x24/edit-delete.gif" href="disconnect" title="${%Disconnect}" />
</j:if>
</l:isAdmin>
</l:tasks>
</l:side-panel>
</j:jelly>
\ No newline at end of file
Back\ to\ List=Retour à la liste
Status=Statut
Build\ History=Historique des builds
Log=
System\ Information=Information système
Disconnect=Déconnection
Back\ to\ List=\u30ea\u30b9\u30c8\u3078\u623b\u308b
Status=\u72b6\u614b
Build\ History=\u30d3\u30eb\u30c9\u5c65\u6b74
Log=\u30ed\u30b0
System\ Information=\u30b7\u30b9\u30c6\u30e0\u60c5\u5831
Disconnect=\u5207\u65ad
Back\ to\ List=Terug naar de lijst
Status=Status
Build\ History=Overzicht bouwpogingen
Log=Log
System\ Information=Systeeminformatie
Disconnect=Loskoppelen
Back\ to\ List=Voltar para a Lista
Status=Estado
Build\ History=Hist\u00F3rico de Constru\u00E7\u00F5es
Log=
System\ Information=Informa\u00E7\u00F5es do Sistema
Disconnect=Desconectar
Back\ to\ List=\u0412\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0441\u043f\u0438\u0441\u043a\u0443
Status=\u0421\u0442\u0430\u0442\u0443\u0441
Build\ History=\u0418\u0441\u0442\u043e\u0440\u0438\u044f \u0441\u0431\u043e\u0440\u043e\u043a
Log=\u0416\u0443\u0440\u043d\u0430\u043b
System\ Information=\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0435
Disconnect=\u041e\u0442\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c
Back\ to\ List=Listeye D\u00f6n
Status=Durum
Build\ History=Yap\u0131land\u0131rma Ge\u00e7mi\u015fi
Log=Log
System\ Information=Sistem Bilgisi
Disconnect=Ba\u011flant\u0131y\u0131 Kes
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson">
<st:contentType value="application/x-java-jnlp-file" />
<j:new var="h" className="hudson.Functions" />
<j:set var="rootURL"
value="${request.scheme}://${request.serverName}:${request.serverPort}${request.contextPath}" />
<!--
See http://www.dallaway.com/acad/webstart/ for obtaining the certificate.
-->
<l:isAdminOrTest test="true">
<!-- See http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/syntax.html for the syntax -->
<jnlp spec="1.0+"
codebase="${rootURL}/computer/${h.encode(it.node.nodeName)}/">
<information>
<title>Slave Agent for ${it.displayName}</title>
<vendor>Hudson project</vendor>
<homepage href="https://hudson.dev.java.net/"/>
</information>
<security>
<all-permissions/>
</security>
<resources>
<j:set var="port" value="${request.getParameter('debugPort')}"/>
<j:choose>
<j:when test="${port!=null}">
<j2se version="1.5+" java-vm-args="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=${port}" />
</j:when>
<j:otherwise>
<j2se version="1.5+" />
</j:otherwise>
</j:choose>
<jar href="jnlpJars/jnlp-agent.jar"/>
<jar href="jnlpJars/remoting.jar"/>
</resources>
<application-desc main-class="hudson.jnlp.Main">
<argument>${request.serverName}</argument>
<argument>${rootURL}/tcpSlaveAgentListener/</argument>
<argument>${app.secretKey}</argument>
<argument>${it.node.nodeName}</argument>
</application-desc>
</jnlp>
</l:isAdminOrTest>
</j:jelly>
<!--
Various system information for diagnostics.
TODO: merge this with Hudson/systemInfo.jelly
-->
<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">
<l:layout title="${it.displayName} System Information">
<st:include page="sidepanel.jelly" />
<l:main-panel>
<l:isAdmin>
<h1>System Properties</h1>
<t:propertyTable items="${it.systemProperties}" />
<h1>Environment Variables</h1>
<t:propertyTable items="${it.envVars}" />
<h1>Thread Dump</h1>
<j:forEach var="t" items="${it.getThreadDump().entrySet()}">
<h2>${t.key}</h2>
<pre>${t.value}</pre>
</j:forEach>
</l:isAdmin>
</l:main-panel>
</l:layout>
</j:jelly>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册