提交 3e5d4929 编写于 作者: R Ryan Campbell

Add CloudProvisioningListener extension point, allowing extensions to block...

Add CloudProvisioningListener extension point, allowing extensions to block cloud provisioning and be notified of provisioning events
上级 2c8da0b8
package hudson.slaves;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Label;
import hudson.model.Node;
import jenkins.model.Jenkins;
import org.jvnet.localizer.Localizable;
import java.util.Collection;
/**
* Allows extensions to be notified of events in any {@link Cloud} and to prevent
* provisioning from a {@link Cloud}.
*
* @author Ryan Campbell
* @since 1.520
*/
public abstract class CloudProvisioningListener implements ExtensionPoint {
/**
* Allows extensions to prevent a cloud from provisioning.
*
* Return null to allow provisioning, or non-null to prevent it.
*
* @param cloud The cloud being provisioned from.
* @param label The label which requires additional capacity. IE,
* the {@link NodeProvisioner#label}.
* May be null if provisioning for unlabeled builds.
* @param numExecutors The number of executors needed.
*
* @return <code>null</code> if provisioning can proceed, or a
* {@link Localizable} reason why it cannot be provisioned.
*/
public Localizable canProvision(Cloud cloud, Label label, int numExecutors) {
return null;
}
/**
* Called after a cloud has returned a PlannedNode, but before
* that node is necessarily ready for connection.
*
* @param cloud the cloud doing the provisioning
* @param label the label which requires additional capacity. IE,
* the {@link NodeProvisioner#label}
* May be null if provisioning for unlabeled builds.
* @param plannedNodes the planned nodes
*
*/
public void onStarted(Cloud cloud, Label label, Collection<NodeProvisioner.PlannedNode> plannedNodes) {
}
/**
* Called when the {@link NodeProvisioner.PlannedNode#future} completes.
* @param plannedNode the plannedNode which resulted in the <code>node</code> being provisioned
* @param node the node which has been provisioned by the cloud
*/
public void onComplete(NodeProvisioner.PlannedNode plannedNode, Node node) {
}
/**
* Called when {@link NodeProvisioner.PlannedNode#future#get()} throws an exception.
*
* @param plannedNode the planned node which failed to launch
* @param t the exception
*/
public void onFailure(NodeProvisioner.PlannedNode plannedNode, Throwable t) {
}
/**
* All the registered {@link CloudProvisioningListener}s.
*/
public static ExtensionList<CloudProvisioningListener> all() {
return Jenkins.getInstance().getExtensionList(CloudProvisioningListener.class);
}
}
......@@ -177,14 +177,22 @@ public class NodeProvisioner {
PlannedNode f = itr.next();
if(f.future.isDone()) {
try {
hudson.addNode(f.future.get());
Node node = f.future.get();
for (CloudProvisioningListener cl : CloudProvisioningListener.all())
cl.onComplete(f,node);
hudson.addNode(node);
LOGGER.info(f.displayName+" provisioning successfully completed. We have now "+hudson.getComputers().length+" computer(s)");
} catch (InterruptedException e) {
throw new AssertionError(e); // since we confirmed that the future is already done
} catch (ExecutionException e) {
LOGGER.log(Level.WARNING, "Provisioned slave "+f.displayName+" failed to launch",e.getCause());
for (CloudProvisioningListener cl : CloudProvisioningListener.all())
cl.onFailure(f,e.getCause());
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Provisioned slave "+f.displayName+" failed to launch",e);
for (CloudProvisioningListener cl : CloudProvisioningListener.all())
cl.onFailure(f,e);
}
f.spent();
......@@ -250,6 +258,8 @@ public class NodeProvisioner {
float m = calcThresholdMargin(totalSnapshot);
if(excessWorkload>1-m) {// and there's more work to do...
LOGGER.fine("Excess workload "+excessWorkload+" detected. (planned capacity="+plannedCapacity+",Qlen="+qlen+",idle="+idle+"&"+idleSnapshot+",total="+totalSnapshot+"m,="+m+")");
CLOUD:
for( Cloud c : hudson.clouds ) {
if(excessWorkload<0) break; // enough slaves allocated
......@@ -260,8 +270,19 @@ public class NodeProvisioner {
// OTOH, because of the exponential decay, even when we need one slave, excess workload is always
// something like 0.95, in which case we want to allocate one node.
// so the threshold here is 1-MARGIN, and hence floor(excessWorkload+MARGIN) is needed to handle this.
Collection<PlannedNode> additionalCapacities = c.provision(label, (int)Math.round(Math.floor(excessWorkload+m)));
int workloadToProvision = (int) Math.round(Math.floor(excessWorkload + m));
for (CloudProvisioningListener cl : CloudProvisioningListener.all())
// consider displaying reasons in a future cloud ux
if (cl.canProvision(c,label,workloadToProvision) != null)
break CLOUD;
Collection<PlannedNode> additionalCapacities = c.provision(label, workloadToProvision);
for (CloudProvisioningListener cl : CloudProvisioningListener.all())
cl.onStarted(c, label, additionalCapacities);
for (PlannedNode ac : additionalCapacities) {
excessWorkload -= ac.numExecutors;
LOGGER.info("Started provisioning "+ac.displayName+" from "+c.name+" with "+ac.numExecutors+" executors. Remaining excess workload:"+excessWorkload);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册