/*
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Erik Ramfelt, Koichi Fujikawa, Red Hat, Inc., Seiji Sogabe,
* Stephen Connolly, Tom Huybrechts, Yahoo! Inc., Alan Harder, CloudBees, Inc.
*
* 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.security.AccessControlled;
import hudson.slaves.ComputerListener;
import hudson.slaves.RetentionStrategy;
import jenkins.model.Jenkins;
import org.kohsuke.stapler.StaplerFallback;
import org.kohsuke.stapler.StaplerProxy;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import jenkins.model.Configuration;
public abstract class AbstractCIBase extends Node implements ItemGroup<TopLevelItem>, StaplerProxy, StaplerFallback, ViewGroup, AccessControlled, DescriptorByNameOwner {
public static boolean LOG_STARTUP_PERFORMANCE = Configuration.getBooleanConfigParameter("logStartupPerformance", false);
private static final Logger LOGGER = Logger.getLogger(AbstractCIBase.class.getName());
/**
* If you are calling this on Hudson something is wrong.
*
* @deprecated
* Maybe you were trying to call {@link #getDisplayName()}.
*/
@Deprecated @Override
public String getNodeName() {
return "";
}
/**
* @deprecated
* Why are you calling a method that always returns ""?
* You probably want to call {@link Jenkins#getRootUrl()}
*/
@Deprecated
public String getUrl() {
return "";
}
/* =================================================================================================================
* Support functions that can only be accessed through package-protected
* ============================================================================================================== */
protected void resetLabel(Label l) {
l.reset();
}
protected void setViewOwner(View v) {
v.owner = this;
}
protected void interruptReloadThread() {
ViewJob.interruptReloadThread();
}
protected void killComputer(Computer c) {
c.kill();
}
/* =================================================================================================================
* Package-protected, but accessed API
* ============================================================================================================== */
/*package*/ final CopyOnWriteArraySet<String> disabledAdministrativeMonitors = new CopyOnWriteArraySet<String>();
/* =================================================================================================================
* Implementation provided
* ============================================================================================================== */
/**
* Returns all {@link Node}s in the system, excluding {@link jenkins.model.Jenkins} instance itself which
* represents the master.
*/
public abstract List<Node> getNodes();
public abstract Queue getQueue();
protected abstract Map<Node,Computer> getComputerMap();
/* =================================================================================================================
* Computer API uses package protection heavily
* ============================================================================================================== */
private void updateComputer(Node n, Map<String,Computer> byNameMap, Set<Computer> used, boolean automaticSlaveLaunch) {
Map<Node,Computer> computers = getComputerMap();
Computer c;
c = byNameMap.get(n.getNodeName());
if (c!=null) {
c.setNode(n); // reuse
} else {
// we always need Computer for the master as a fallback in case there's no other Computer.
if(n.getNumExecutors()>0 || n==Jenkins.getInstance()) {
computers.put(n, c = n.createComputer());
if (!n.isHoldOffLaunchUntilSave() && automaticSlaveLaunch) {
RetentionStrategy retentionStrategy = c.getRetentionStrategy();
if (retentionStrategy != null) {
// if there is a retention strategy, it is responsible for deciding to start the computer
retentionStrategy.start(c);
} else {
// we should never get here, but just in case, we'll fall back to the legacy behaviour
c.connect(true);
}
}
}
}
used.add(c);
}
/*package*/ void removeComputer(final Computer computer) {
Queue.withLock(new Runnable() {
@Override
public void run() {
Map<Node,Computer> computers = getComputerMap();
for (Map.Entry<Node, Computer> e : computers.entrySet()) {
if (e.getValue() == computer) {
computers.remove(e.getKey());
computer.onRemoved();
return;
}
}
}
});
}
/*package*/ @CheckForNull Computer getComputer(Node n) {
Map<Node,Computer> computers = getComputerMap();
return computers.get(n);
}
/**
* Updates Computers.
*
* <p>
* This method tries to reuse existing {@link Computer} objects
* so that we won't upset {@link Executor}s running in it.
*/
protected void updateComputerList(final boolean automaticSlaveLaunch) {
final Map<Node,Computer> computers = getComputerMap();
final Set<Computer> old = new HashSet<Computer>(computers.size());
Queue.withLock(new Runnable() {
@Override
public void run() {
Map<String,Computer> byName = new HashMap<String,Computer>();
for (Computer c : computers.values()) {
old.add(c);
Node node = c.getNode();
if (node == null)
continue; // this computer is gone
byName.put(node.getNodeName(),c);
}
Set<Computer> used = new HashSet<Computer>(old.size());
updateComputer(AbstractCIBase.this, byName, used, automaticSlaveLaunch);
for (Node s : getNodes()) {
long start = System.currentTimeMillis();
updateComputer(s, byName, used, automaticSlaveLaunch);
if(LOG_STARTUP_PERFORMANCE)
LOGGER.info(String.format("Took %dms to update node %s",
System.currentTimeMillis()-start, s.getNodeName()));
}
// find out what computers are removed, and kill off all executors.
// when all executors exit, it will be removed from the computers map.
// so don't remove too quickly
old.removeAll(used);
// we need to start the process of reducing the executors on all computers as distinct
// from the killing action which should not excessively use the Queue lock.
for (Computer c : old) {
c.inflictMortalWound();
}
}
});
for (Computer c : old) {
// when we get to here, the number of executors should be zero so this call should not need the Queue.lock
killComputer(c);
}
getQueue().scheduleMaintenance();
for (ComputerListener cl : ComputerListener.all())
cl.onConfigurationChange();
}
}