/* * The MIT License * * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly, Thomas J. Black * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.model; import hudson.BulkChange; import hudson.DescriptorExtensionList; import hudson.Extension; import hudson.Util; import hudson.XmlFile; import hudson.init.Initializer; import hudson.model.Descriptor.FormException; import hudson.model.listeners.SaveableListener; import hudson.node_monitors.NodeMonitor; import hudson.slaves.NodeDescriptor; import hudson.triggers.SafeTimerTask; import hudson.util.DescribableList; import hudson.util.FormApply; import hudson.util.FormValidation; import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; import jenkins.model.ModelObjectWithContextMenu.ContextMenu; import jenkins.util.Timer; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import org.kohsuke.stapler.interceptor.RequirePOST; import javax.servlet.ServletException; import java.io.File; import java.io.IOException; import java.util.AbstractList; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.json.JSONObject; import static hudson.init.InitMilestone.JOB_LOADED; /** * Serves as the top of {@link Computer}s in the URL hierarchy. *
* Getter methods are prefixed with '_' to avoid collision with computer names.
*
* @author Kohsuke Kawaguchi
*/
@ExportedBean
public final class ComputerSet extends AbstractModelObject implements Describable
* This excludes executors that belong to offline nodes.
*/
@Exported
public int getTotalExecutors() {
int r=0;
for (Computer c : get_all()) {
if(c.isOnline())
r += c.countExecutors();
}
return r;
}
/**
* Number of busy {@link Executor}s that are carrying out some work right now.
*/
@Exported
public int getBusyExecutors() {
int r=0;
for (Computer c : get_all()) {
if(c.isOnline())
r += c.countBusy();
}
return r;
}
/**
* {@code getTotalExecutors()-getBusyExecutors()}, plus executors that are being brought online.
*/
public int getIdleExecutors() {
int r=0;
for (Computer c : get_all())
if((c.isOnline() || c.isConnecting()) && c.isAcceptingTasks())
r += c.countIdle();
return r;
}
public String getSearchUrl() {
return "/computers/";
}
public Computer getDynamic(String token, StaplerRequest req, StaplerResponse rsp) {
return Jenkins.getInstance().getComputer(token);
}
public void do_launchAll(StaplerRequest req, StaplerResponse rsp) throws IOException {
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
for(Computer c : get_all()) {
if(c.isLaunchSupported())
c.connect(true);
}
rsp.sendRedirect(".");
}
/**
* Triggers the schedule update now.
*
* TODO: ajax on the client side to wait until the update completion might be nice.
*/
public void doUpdateNow( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
for (NodeMonitor nodeMonitor : NodeMonitor.getAll()) {
Thread t = nodeMonitor.triggerUpdate();
String columnCaption = nodeMonitor.getColumnCaption();
if (columnCaption != null) {
t.setName(columnCaption);
}
}
rsp.forwardToPreviousPage(req);
}
/**
* First check point in creating a new slave.
*/
public synchronized void doCreateItem( StaplerRequest req, StaplerResponse rsp,
@QueryParameter String name, @QueryParameter String mode,
@QueryParameter String from ) throws IOException, ServletException {
final Jenkins app = Jenkins.getInstance();
app.checkPermission(Computer.CREATE);
if(mode!=null && mode.equals("copy")) {
name = checkName(name);
Node src = app.getNode(from);
if(src==null) {
if (Util.fixEmpty(from) == null) {
throw new Failure(Messages.ComputerSet_SpecifySlaveToCopy());
} else {
throw new Failure(Messages.ComputerSet_NoSuchSlave(from));
}
}
// copy through XStream
String xml = Jenkins.XSTREAM.toXML(src);
Node result = (Node) Jenkins.XSTREAM.fromXML(xml);
result.setNodeName(name);
if(result instanceof Slave){ //change userId too
User user = User.current();
((Slave)result).setUserId(user==null ? "anonymous" : user.getId());
}
result.holdOffLaunchUntilSave = true;
app.addNode(result);
// send the browser to the config page
rsp.sendRedirect2(result.getNodeName()+"/configure");
} else {
// proceed to step 2
if (mode == null) {
throw new Failure("No mode given");
}
NodeDescriptor d = NodeDescriptor.all().findByName(mode);
if (d == null) {
throw new Failure("No node type ‘" + mode + "’ is known");
}
d.handleNewNodePage(this,name,req,rsp);
}
}
/**
* Really creates a new slave.
*/
public synchronized void doDoCreateItem( StaplerRequest req, StaplerResponse rsp,
@QueryParameter String name,
@QueryParameter String type ) throws IOException, ServletException, FormException {
final Jenkins app = Jenkins.getInstance();
app.checkPermission(Computer.CREATE);
String fixedName = Util.fixEmptyAndTrim(name);
checkName(fixedName);
JSONObject formData = req.getSubmittedForm();
formData.put("name", fixedName);
Node result = NodeDescriptor.all().find(type).newInstance(req, formData);
app.addNode(result);
// take the user back to the slave list top page
rsp.sendRedirect2(".");
}
/**
* Makes sure that the given name is good as a slave name.
* @return trimmed name if valid; throws ParseException if not
*/
public String checkName(String name) throws Failure {
if(name==null)
throw new Failure("Query parameter 'name' is required");
name = name.trim();
Jenkins.checkGoodName(name);
if(Jenkins.getInstance().getNode(name)!=null)
throw new Failure(Messages.ComputerSet_SlaveAlreadyExists(name));
// looks good
return name;
}
/**
* Makes sure that the given name is good as a slave name.
*/
public FormValidation doCheckName(@QueryParameter String value) throws IOException, ServletException {
Jenkins.getInstance().checkPermission(Computer.CREATE);
if(Util.fixEmpty(value)==null)
return FormValidation.ok();
try {
checkName(value);
return FormValidation.ok();
} catch (Failure e) {
return FormValidation.error(e.getMessage());
}
}
/**
* Accepts submission from the configuration page.
*/
@RequirePOST
public synchronized HttpResponse doConfigSubmit( StaplerRequest req) throws IOException, ServletException, FormException {
BulkChange bc = new BulkChange(MONITORS_OWNER);
try {
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
monitors.rebuild(req,req.getSubmittedForm(),getNodeMonitorDescriptors());
// add in the rest of instances are ignored instances
for (Descriptor