Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 'master' of
git://github.com/bryandagnin/libvirt-slave-plugin into
bryandagnin-master

(Implementing JENKINS-17293)
  • Loading branch information
tastybug committed Mar 20, 2013
2 parents 4b7a9d5 + daf8b87 commit a72d0c0
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 3 deletions.
69 changes: 68 additions & 1 deletion src/main/java/hudson/plugins/libvirt/Hypervisor.java
Expand Up @@ -32,6 +32,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -62,10 +63,13 @@ public class Hypervisor extends Cloud {
private final String hypervisorSystemUrl;
private final int hypervisorSshPort;
private final String username;
private final int maxOnlineSlaves;
private transient int currentOnlineSlaveCount = 0;
private transient Hashtable<String, String> currentOnline;
private Connect connection;

@DataBoundConstructor
public Hypervisor(String hypervisorType, String hypervisorHost, int hypervisorSshPort, String hypervisorSystemUrl, String username) {
public Hypervisor(String hypervisorType, String hypervisorHost, int hypervisorSshPort, String hypervisorSystemUrl, String username, int maxOnlineSlaves) {
super("Hypervisor(libvirt)");
this.hypervisorType = hypervisorType;
this.hypervisorHost = hypervisorHost;
Expand All @@ -76,6 +80,12 @@ public Hypervisor(String hypervisorType, String hypervisorHost, int hypervisorSs
}
this.hypervisorSshPort = hypervisorSshPort <= 0 ? 22 : hypervisorSshPort;
this.username = username;
this.maxOnlineSlaves = maxOnlineSlaves;
}

protected void EnsureLists() {
if (currentOnline == null)
currentOnline = new Hashtable<String, String>();
}

private synchronized Connect getOrCreateConnection() {
Expand Down Expand Up @@ -120,6 +130,14 @@ public String getUsername() {
return username;
}

public int getMaxOnlineSlaves() {
return maxOnlineSlaves;
}

public synchronized int getCurrentOnlineSlaveCount() {
return currentOnlineSlaveCount;
}

public String getHypervisorDescription() {
return getHypervisorType() + " - " + getHypervisorHost();
}
Expand Down Expand Up @@ -233,6 +251,55 @@ public String toString() {
return sb.toString();
}

public synchronized Boolean canMarkVMOnline(String slaveName, String vmName) {
EnsureLists();

// Don't allow more than max.
if ((maxOnlineSlaves > 0) && (currentOnline.size() == maxOnlineSlaves))
return Boolean.FALSE;

// Don't allow two slaves to the same VM to fire up.
if (currentOnline.containsValue(vmName))
return Boolean.FALSE;

// Don't allow two instances of the same slave, although Jenkins will
// probably not encounter this.
if (currentOnline.containsKey(slaveName))
return Boolean.FALSE;

// Don't allow a misconfigured slave to try start
if ("".equals(vmName) || "".equals(slaveName)) {
LogRecord rec = new LogRecord(Level.WARNING, "Slave '"+slaveName+"' (using VM '"+vmName+"') appears to be misconfigured.");
LOGGER.log(rec);
return Boolean.FALSE;
}

return Boolean.TRUE;
}

public synchronized Boolean markVMOnline(String slaveName, String vmName) {
EnsureLists();

// If the combination is already in the list, it's good.
if (currentOnline.containsKey(slaveName) && currentOnline.get(slaveName).equals(vmName))
return Boolean.TRUE;

if (!canMarkVMOnline(slaveName, vmName))
return Boolean.FALSE;

currentOnline.put(slaveName, vmName);
currentOnlineSlaveCount++;

return Boolean.TRUE;
}

public synchronized void markVMOffline(String slaveName, String vmName) {
EnsureLists();

if (currentOnline.remove(slaveName) != null)
currentOnlineSlaveCount--;
}

@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/hudson/plugins/libvirt/VirtualMachineLauncher.java
Expand Up @@ -87,11 +87,29 @@ public VirtualMachine getVirtualMachine() {
return virtualMachine;
}

public String getVirtualMachineName() {
return virtualMachineName;
}

@Override
public boolean isLaunchSupported() {
return delegate.isLaunchSupported();
}

public Hypervisor findOurHypervisorInstance() throws RuntimeException {
if (hypervisorDescription != null && virtualMachineName != null) {
Hypervisor hypervisor = null;
for (Cloud cloud : Hudson.getInstance().clouds) {
if (cloud instanceof Hypervisor && ((Hypervisor) cloud).getHypervisorDescription().equals(hypervisorDescription)) {
hypervisor = (Hypervisor) cloud;
return hypervisor;
}
}
}
LOGGER.log(Level.INFO, "Could not find our libvirt cloud instance!");
throw new RuntimeException("Could not find our libvirt cloud instance!");
}

@Override
public void launch(SlaveComputer slaveComputer, TaskListener taskListener)
throws IOException, InterruptedException {
Expand Down Expand Up @@ -154,6 +172,9 @@ public synchronized void afterDisconnect(SlaveComputer slaveComputer, TaskListen
} else {
taskListener.getLogger().println("...Virtual machine found; it is already suspended, no shutdown required.");
}
VirtualMachineLauncher vmL = (VirtualMachineLauncher) ((SlaveComputer) slaveComputer).getLauncher();
Hypervisor vmC = vmL.findOurHypervisorInstance();
vmC.markVMOffline(slaveComputer.getDisplayName(), vmL.getVirtualMachineName());
return;
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/hudson/plugins/libvirt/VirtualMachineSlave.java
Expand Up @@ -21,15 +21,20 @@
*/
package hudson.plugins.libvirt;

import hudson.AbortException;
import hudson.Extension;
import hudson.Util;
import hudson.model.TaskListener;
import hudson.model.Computer;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.model.Slave;
import hudson.slaves.Cloud;
import hudson.slaves.ComputerListener;
import hudson.slaves.NodeProperty;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.RetentionStrategy;
import hudson.slaves.SlaveComputer;
import hudson.util.ListBoxModel;

import java.io.IOException;
Expand Down Expand Up @@ -85,6 +90,25 @@ public ComputerLauncher getDelegateLauncher() {

public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();

@Extension
public static class VirtualMachineComputerListener extends ComputerListener {

@Override
public void preLaunch(Computer c, TaskListener taskListener) throws IOException, InterruptedException {
/* We may be called on any slave type so check that we should
* be in here. */
if (!(c.getNode() instanceof VirtualMachineSlave)) {
return;
}

VirtualMachineLauncher vmL = (VirtualMachineLauncher) ((SlaveComputer) c).getLauncher();
Hypervisor vmC = vmL.findOurHypervisorInstance();

if (!vmC.markVMOnline(c.getDisplayName(), vmL.getVirtualMachineName()))
throw new AbortException("Capacity threshold (" + vmC.getMaxOnlineSlaves() + ") reached at hypervisor \"" + vmC.getHypervisorDescription() + "\", slave commissioning delayed.");
}
}

@Extension
public static final class DescriptorImpl extends SlaveDescriptor {

Expand Down
@@ -1,5 +1,5 @@
<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">
<f:entry title="${%Computer Name}" field="hypervisorType" name="hypervisorType" help="/plugin/libvirt-slave/help-libvirt-hypervisorType.html">
<f:entry title="${%Hypervisor Type}" field="hypervisorType" name="hypervisorType" help="/plugin/libvirt-slave/help-libvirt-hypervisorType.html">
<select class="setting-input" name="hypervisorType">
<j:forEach var="t" items="${descriptor.getHypervisorTypes()}" varStatus="loop">
<f:option value="${t}" selected="${t==instance.hypervisorType}">${t}</f:option>
Expand All @@ -16,9 +16,12 @@
<f:entry title="${%SSH Port}" field="hypervisorSshPort" help="/plugin/libvirt-slave/help-libvirt-hypervisorSshPort.html">
<f:textbox default="22"/>
</f:entry>
<f:entry title="${%Hypervisor System Url}" field="hypervisorSystemUrl">
<f:entry title="${%Hypervisor System URL}" field="hypervisorSystemUrl">
<f:textbox default="system"/>
</f:entry>
<f:entry title="${%Concurrent Slaves Capacity}" help="/plugin/libvirt-slave/help-libvirt-maxOnlineSlaves.html">
<f:textbox clazz="required number" field="maxOnlineSlaves" />
</f:entry>
</f:advanced>
<f:validateButton title="${%Test Connection}" progress="${%Testing...}" method="testConnection" with="hypervisorType,hypervisorHost,username,hypervisorSshPort,hypervisorSystemUrl"/>
</j:jelly>
2 changes: 2 additions & 0 deletions src/main/webapp/help-libvirt-maxOnlineSlaves.html
@@ -0,0 +1,2 @@
<div>Maximum number of virtual machines that this libvirt cloud should use at one time. A value of 0 indicates no maximum value.<br>
Note: If the hypervisor reaches its capacity, Jenkins will keep reissuing the slave commissioning until the capacity is below the threshold. Associated build jobs are thus delayed but not failed by a lack of slaves.</div>

0 comments on commit a72d0c0

Please sign in to comment.