/** * Copyright (C) 2010, Byte-Code srl <http://www.byte-code.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Date: Mar 04, 2010 * Author: Marco Mornati<mmornati@byte-code.com> */ package hudson.plugins.libvirt; import hudson.util.FormValidation; import hudson.model.Descriptor; import hudson.model.Label; import hudson.Extension; import hudson.slaves.Cloud; import hudson.slaves.NodeProvisioner; import java.util.ArrayList; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import java.util.Map; import java.util.HashMap; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import java.util.logging.Level; import java.util.logging.LogRecord; import javax.servlet.ServletException; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; import org.libvirt.Connect; import org.libvirt.Domain; import org.libvirt.LibvirtException; /** * Represents a virtual datacenter. */ public class Hypervisor extends Cloud { private static final Logger LOGGER = Logger.getLogger(Hypervisor.class.getName()); private final String hypervisorType; private final String hypervisorHost; private final String hypervisorSystemUrl; private final int hypervisorSshPort; private final String username; private transient Map<String, Domain> domains = null; private transient List<VirtualMachine> virtualMachineList = null; private transient Connect hypervisorConnection = null; @DataBoundConstructor public Hypervisor(String hypervisorType, String hypervisorHost, int hypervisorSshPort, String hypervisorSystemUrl, String username) { super("Hypervisor(libvirt)"); this.hypervisorType = hypervisorType; this.hypervisorHost = hypervisorHost; if (hypervisorSystemUrl != null && !hypervisorSystemUrl.equals("")) { this.hypervisorSystemUrl = hypervisorSystemUrl; } else { this.hypervisorSystemUrl = "system"; } this.hypervisorSshPort = hypervisorSshPort <= 0 ? 22 : hypervisorSshPort; this.username = username; virtualMachineList = retrieveVirtualMachines(); } private Connect makeConnection() { String hypervisorUri = constructHypervisorURI(); LOGGER.log(Level.INFO, "Trying to establish a connection to hypervisor URI: {0} as {1}/******", new Object[]{hypervisorUri, username}); if (hypervisorConnection == null) { try { hypervisorConnection = new Connect(hypervisorUri, false); LOGGER.log(Level.INFO, "Established connection to hypervisor URI: {0} as {1}/******", new Object[]{hypervisorUri, username}); } catch (LibvirtException e) { LogRecord rec = new LogRecord(Level.WARNING, "Failed to establish connection to hypervisor URI: {0} as {1}/******"); rec.setThrown(e); rec.setParameters(new Object[]{hypervisorUri, username}); LOGGER.log(rec); } } return hypervisorConnection; } private List<VirtualMachine> retrieveVirtualMachines() { List<VirtualMachine> vmList = new ArrayList<VirtualMachine>(); try { domains = getDomains(); for (String domainName : domains.keySet()) { vmList.add(new VirtualMachine(this, domainName)); } } catch (Exception e) { LogRecord rec = new LogRecord(Level.SEVERE, "Cannot connect to datacenter {0} as {1}/******"); rec.setThrown(e); rec.setParameters(new Object[]{hypervisorHost, username}); LOGGER.log(rec); } return vmList; } public String getHypervisorHost() { return hypervisorHost; } public int getHypervisorSshPort() { return hypervisorSshPort; } public String getHypervisorType() { return hypervisorType; } public String getHypervisorSystemUrl() { return hypervisorSystemUrl; } public String getUsername() { return username; } public String getHypervisorDescription() { return getHypervisorType() + " - " + getHypervisorHost(); } public synchronized Map<String, Domain> getDomains() throws LibvirtException { Map<String, Domain> domains = new HashMap<String, Domain>(); hypervisorConnection = makeConnection(); LogRecord info = new LogRecord(Level.INFO, "Getting hypervisor domains"); LOGGER.log(info); if (hypervisorConnection != null) { for (String c : hypervisorConnection.listDefinedDomains()) { if (c != null && !c.equals("")) { Domain domain = null; try { domain = hypervisorConnection.domainLookupByName(c); domains.put(domain.getName(), domain); } catch (Exception e) { LogRecord rec = new LogRecord(Level.INFO, "Error retreiving information for domain with name: {0}"); rec.setParameters(new Object[]{c}); rec.setThrown(e); LOGGER.log(rec); } } } for (int c : hypervisorConnection.listDomains()) { Domain domain = null; try { domain = hypervisorConnection.domainLookupByID(c); domains.put(domain.getName(), domain); } catch (Exception e) { LogRecord rec = new LogRecord(Level.INFO, "Error retreiving information for domain with id: {0}"); rec.setParameters(new Object[]{c}); rec.setThrown(e); LOGGER.log(rec); } } } else { LogRecord rec = new LogRecord(Level.SEVERE, "Cannot connect to datacenter {0} as {1}/******"); rec.setParameters(new Object[]{hypervisorHost, username}); LOGGER.log(rec); } return domains; } public synchronized List<VirtualMachine> getVirtualMachines() { if (virtualMachineList == null) { virtualMachineList = retrieveVirtualMachines(); } return virtualMachineList; } public Collection<NodeProvisioner.PlannedNode> provision(Label label, int i) { return Collections.emptySet(); } public boolean canProvision(Label label) { return false; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("Hypervisor"); sb.append("{hypervisorUri='").append(hypervisorHost).append('\''); sb.append(", username='").append(username).append('\''); sb.append('}'); return sb.toString(); } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } public String constructHypervisorURI() { return hypervisorType.toLowerCase() + "+ssh://" + username + "@" + hypervisorHost + ":" + hypervisorSshPort + "/" + hypervisorSystemUrl + "?no_tty=1"; } @Extension public static final class DescriptorImpl extends Descriptor<Cloud> { public final ConcurrentMap<String, Hypervisor> hypervisors = new ConcurrentHashMap<String, Hypervisor>(); private String hypervisorType; private String hypervisorHost; private String hypervisorSystemUrl; private int hypervisorSshPort; private String username; public String getDisplayName() { return "Hypervisor (via libvirt)"; } @Override public boolean configure(StaplerRequest req, JSONObject o) throws FormException { hypervisorType = o.getString("hypervisorType"); hypervisorHost = o.getString("hypervisorHost"); hypervisorSystemUrl = o.getString("hypervisorSystemUrl"); hypervisorSshPort = o.getInt("hypervisorSshPort"); username = o.getString("username"); save(); return super.configure(req, o); } public FormValidation doTestConnection( @QueryParameter String hypervisorType, @QueryParameter String hypervisorHost, @QueryParameter String hypervisorSshPort, @QueryParameter String username, @QueryParameter String hypervisorSystemUrl) throws Exception, ServletException { try { if (hypervisorHost == null) { return FormValidation.error("Hypervisor Host is not specified"); } if (hypervisorType == null) { return FormValidation.error("Hypervisor type is not specified"); } if (username == null) { return FormValidation.error("Username is not specified"); } String hypervisorUri = hypervisorType.toLowerCase() + "+ssh://" + username + "@" + hypervisorHost + ":" + hypervisorSshPort + "/" + hypervisorSystemUrl + "?no_tty=1"; LogRecord rec = new LogRecord(Level.INFO, "Testing connection to hypervisor: {0}"); rec.setParameters(new Object[]{hypervisorUri}); LOGGER.log(rec); Connect hypervisorConnection = new Connect(hypervisorUri, false); hypervisorConnection.close(); return FormValidation.ok("Connected successfully"); } catch (LibvirtException e) { LogRecord rec = new LogRecord(Level.WARNING, "Failed to check hypervisor connection to {0} as {1}/******"); rec.setThrown(e); rec.setParameters(new Object[]{hypervisorHost, username}); LOGGER.log(rec); return FormValidation.error(e.getMessage()); } catch (UnsatisfiedLinkError e) { LogRecord rec = new LogRecord(Level.WARNING, "Failed to connect to hypervisor. Check libvirt installation on hudson machine!"); rec.setThrown(e); rec.setParameters(new Object[]{hypervisorHost, username}); LOGGER.log(rec); return FormValidation.error(e.getMessage()); } } public String getHypervisorHost() { return hypervisorHost; } public int getHypervisorSshPort() { return hypervisorSshPort; } public String getHypervisorSystemUrl() { return hypervisorSystemUrl; } public String getHypervisorType() { return hypervisorType; } public String getUsername() { return username; } public List<String> getHypervisorTypes() { List<String> types = new ArrayList<String>(); types.add("QEMU"); types.add("XEN"); return types; } } }