package hudson.plugins.jclouds; import hudson.Extension; import hudson.Util; import hudson.model.Describable; import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.model.Label; import hudson.model.Node; import hudson.model.TaskListener; import hudson.util.ListBoxModel; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.jclouds.compute.ComputeService; import org.jclouds.compute.domain.Architecture; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.rest.AuthorizationException; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; /** * * @author Monty Taylor */ public class JCloudTemplate implements Describable<JCloudTemplate> { private final String slave; private final String description; private final String remoteFS; private final String labels; private final String image; private final String architectureString; private String numExecutors; private transient /*almost final*/ Set<Label> labelSet; private transient Architecture architecture; private final String initScript; /*private final String userData; private final String remoteAdmin; private final String rootCommandPrefix;*/ private transient JClouds parent; @DataBoundConstructor public JCloudTemplate(String slave, String description, /*String remoteFS,*/ String labelString, /*String image, */ /*String architectureString, */ String numExecutors/* , String initScript, String userData, String remoteAdmin, String rootCommandPrefix*/) { this.slave = slave; this.description = description; this.remoteFS = "/var/lib/hudson"; this.labels = Util.fixNull(labelString); this.image = null; this.architectureString = Architecture.X86_64.toString(); // this.image = image; // this.architectureString = architectureString; this.numExecutors = numExecutors; this.initScript = "aptitude update; aptitude install -y sun-sun-java6-jdk ; mkdir -p /var/lib/hudson"; /* this.userData = userData; this.remoteAdmin = remoteAdmin; this.rootCommandPrefix = rootCommandPrefix;*/ readResolve(); } private static final Logger LOGGER = Logger.getLogger(JClouds.class.getName()); /** * Initializes data structure that we don't persist. */ protected final Object readResolve() { labelSet = Label.parse(labels); architecture = Architecture.valueOf(architectureString); return this; } public String getDescription() { return description; } public String getRemoteFS() { return remoteFS; } public String getLabels() { return labels; } public String getImage() { return image; } public String getSlave() { return slave; } public int getNumExecutors() { if (numExecutors == null) return 1; try { return Integer.parseInt(numExecutors); } catch (NumberFormatException e) { /* @TODO: Make this based on number of CPUs (see EC2) */ return 1; } } public Architecture getArchitecture() { if (architecture == null) { architecture= Architecture.valueOf(architectureString); } return architecture; } public void setNumExecutors(String numExecutors) { this.numExecutors = numExecutors; } void buildTemplate(JClouds parent) { this.parent = parent; } public JClouds getParent() { return parent; } public void setParent(JClouds parent) { this.parent = parent; } public static String getSshKey() throws IOException { File id_rsa_pub = new File(System.getProperty("user.home") + File.separator + ".ssh" + File.separator + "id_rsa.pub"); BufferedReader irp = new BufferedReader(new FileReader(id_rsa_pub)); String line; String key = ""; while ((line = irp.readLine()) != null) { key += line; } return key; } /** * Provisions a new Compute Service * * @return always non-null. This needs to be then added to {@link Hudson#addNode(Node)}. */ public List<JCloudSlave> provision(TaskListener listener, int requestedWorkload) throws AuthorizationException, Throwable { PrintStream logger = listener.getLogger(); try { logger.println("Launching " + slave); ComputeService client = getParent().connect(); TemplateOptions options = new TemplateOptions(); options.runScript(initScript.getBytes()); options.inboundPorts(22, 8080); options.authorizePublicKey(getSshKey()); TemplateBuilder builder = client.templateBuilder(); builder.options(options); builder.architecture(getArchitecture()); builder.osFamily(OsFamily.UBUNTU); builder.minRam(512); /* @TODO We should include our options here! */ Set<? extends NodeMetadata> results = client.runNodesWithTag(slave, requestedWorkload, builder.build()); /* Instance inst = ec2.runInstances(ami, 1, 1, Collections.<String>emptyList(), userData, keyPair.getKeyName(), type).getInstances().get(0); return newSlave(inst); */ return newSlaves(results, client); } catch (Descriptor.FormException e) { throw new AssertionError(); // we should have discovered all configuration issues upfront } } private List<JCloudSlave> newSlaves(Set<? extends NodeMetadata> nodes, ComputeService client) throws Descriptor.FormException, IOException { List<JCloudSlave> slaves = new ArrayList<JCloudSlave>(nodes.size()); for (NodeMetadata n : nodes) { /* @TODO: Actually create a real slave here */ slaves.add(new JCloudSlave(n.getId(), getDescription(), getRemoteFS(), n.getLocation(), labels, client, n)); } return slaves; } /** * Does this contain the given label? * * @param l * can be null to indicate "don't care". */ public boolean containsLabel(Label l) { return l==null || labelSet.contains(l); } public Descriptor<JCloudTemplate> getDescriptor() { return Hudson.getInstance().getDescriptor(getClass()); } @Extension public static final class DescriptorImpl extends Descriptor<JCloudTemplate> { @Override public String getDisplayName() { return null; } public ListBoxModel doFillImageItems(@QueryParameter String provider, @QueryParameter String user, @QueryParameter String secret) { LOGGER.log(Level.INFO, "Enter doFillImageItems"); ListBoxModel m = new ListBoxModel(); ComputeService client = null; try { client = JClouds.getComputeService(provider, user, secret); } catch (Throwable ex) { LOGGER.log(Level.INFO, "compute service problem {0}", ex.getLocalizedMessage()); return m; } for (Image image : client.listImages()) { m.add(image.getDescription(), image.getId()); LOGGER.log(Level.INFO, "image: {0}|{1}|{2}:{3}", new Object[]{ image.getArchitecture(), image.getOsFamily(), image.getOsDescription(), image.getDescription() }); } return m; } } }