Package hudson.plugins.libvirt

Source Code of hudson.plugins.libvirt.Hypervisor$DescriptorImpl

/**
*  Copyright (C) 2010, Byte-Code srl <http://www.byte-code.com>
*  Copyright (C) 2012  Philipp Bartsch <tastybug@tastybug.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>
* @author Philipp Bartsch <tastybug@tastybug.com>
*/
package hudson.plugins.libvirt;

import com.cloudbees.jenkins.plugins.sshcredentials.SSHAuthenticator;
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserListBoxModel;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.domains.SchemeRequirement;
import com.google.common.base.Strings;
import com.trilead.ssh2.Connection;
import hudson.Extension;
import hudson.model.AbstractProject;
import hudson.model.Descriptor;
import hudson.model.ItemGroup;
import hudson.model.Label;
import hudson.plugins.libvirt.lib.ConnectionBuilder;
import hudson.plugins.libvirt.lib.IConnect;
import hudson.plugins.libvirt.lib.IDomain;
import hudson.plugins.libvirt.lib.VirtException;
import hudson.plugins.libvirt.lib.libvirt.LibVirtConnectImpl;
import hudson.security.ACL;
import hudson.slaves.Cloud;
import hudson.slaves.NodeProvisioner;
import hudson.util.FormValidation;

import java.util.ArrayList;
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;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import javax.servlet.ServletException;

import hudson.util.ListBoxModel;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;

import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

/**
* 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 final int maxOnlineSlaves;
    private transient int currentOnlineSlaveCount = 0;
    private transient Hashtable<String, String> currentOnline;
    private transient IConnect connection;
    private final boolean useNativeJavaConnection;
    private final String credentialsId;


    @DataBoundConstructor
    public Hypervisor(String hypervisorType, String hypervisorHost, int hypervisorSshPort, String hypervisorSystemUrl, String username, int maxOnlineSlaves,
                      boolean useNativeJavaConnection, String credentialsId) {
        super("Hypervisor(libvirt)");
        this.hypervisorType = hypervisorType;
        this.hypervisorHost = hypervisorHost;
        if (hypervisorSystemUrl != null) {
            this.hypervisorSystemUrl = hypervisorSystemUrl;
        } else {
            this.hypervisorSystemUrl = "system";
        }
        this.hypervisorSshPort = hypervisorSshPort <= 0 ? 22 : hypervisorSshPort;
        this.username = username;
        this.maxOnlineSlaves = maxOnlineSlaves;
        this.useNativeJavaConnection = useNativeJavaConnection;
        this.credentialsId = credentialsId;
    }

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

    private ConnectionBuilder createBuilder() {
        return ConnectionBuilder.newBuilder()
                .hypervisorType(hypervisorType)
                .userName(username)
                .withCredentials(lookupSystemCredentials(credentialsId))
                .hypervisorHost(hypervisorHost)
                .hypervisorPort(hypervisorSshPort)
                .hypervisorSysUrl(hypervisorSystemUrl)
                .useNativeJava(useNativeJavaConnection);
    }

    private synchronized IConnect getOrCreateConnection() throws VirtException {

      if (connection == null || !connection.isConnected()) {

            ConnectionBuilder builder = createBuilder();

          LOGGER.log(Level.INFO, "Trying to establish a connection to hypervisor URI: {0} as {1}/******",
                  new Object[]{builder.constructHypervisorURI(), username});
         
          try {
              connection = builder.build();

              LOGGER.log(Level.INFO, "Established connection to hypervisor URI: {0} as {1}/******",
                      new Object[]{builder.constructHypervisorURI(), username});
          } catch (VirtException e) {
              LogRecord rec = new LogRecord(Level.SEVERE, "Failed to establish connection to hypervisor URI: {0} as {1}/******");
              rec.setThrown(e);
              rec.setParameters(new Object[]{builder.constructHypervisorURI(), username});
              LOGGER.log(rec);
          }
      } else {
        try {
          // the connection appears to be up but might actually be dead (e.g. due to a restart of libvirtd)
          // lets try a simple function call and see if it turns out ok
          connection.getVersion();
        } catch (VirtException lve) {
                ConnectionBuilder builder = createBuilder();
          LogRecord rec = new LogRecord(Level.WARNING, "Connection appears to be broken, trying to reconnect: {0} as {1}/******");
              rec.setParameters(new Object[]{builder.constructHypervisorURI(), username});
              LOGGER.log(rec);
              try {
                    connection = builder.build();
              } catch (VirtException lve2) {
                rec = new LogRecord(Level.SEVERE, "Failed to re-establish connection to hypervisor URI: {0} as {1}/******");
                rec.setThrown(lve2);
                rec.setParameters(new Object[]{builder.constructHypervisorURI(), username});
                LOGGER.log(rec);
              }
        }
      }
        return connection;
    }



    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 boolean isUseNativeJavaConnection() {
        return useNativeJavaConnection;
    }

    public String getCredentialsId() {
        return credentialsId;
    }

    public int getMaxOnlineSlaves() {
        return maxOnlineSlaves;
    }

    public synchronized int getCurrentOnlineSlaveCount() {
        return currentOnlineSlaveCount;
    }

    public String getHypervisorDescription() {
        return getHypervisorType() + " - " + getHypervisorHost();
    }

    public synchronized Map<String, IDomain> getDomains() throws VirtException {
        Map<String, IDomain> domains = new HashMap<String, IDomain>();
        IConnect con = getOrCreateConnection();
        LogRecord info = new LogRecord(Level.FINE, "Getting hypervisor domains.");
        LOGGER.log(info);
        if (con != null) {
            for (String c : con.listDefinedDomains()) {
                if (c != null && !c.equals("")) {
                    IDomain domain = null;
                    try {
                        domain = con.domainLookupByName(c);
                        domains.put(domain.getName(), domain);
                    } catch (Exception e) {
                        LogRecord rec = new LogRecord(Level.WARNING, "Error retrieving information for domain with name: {0}.");
                        rec.setParameters(new Object[]{c});
                        rec.setThrown(e);
                        LOGGER.log(rec);
                    }
                }
            }
            for (int c : con.listDomains()) {
                IDomain domain = null;
                try {
                    domain = con.domainLookupByID(c);
                    domains.put(domain.getName(), domain);
                } catch (Exception e) {
                    LogRecord rec = new LogRecord(Level.WARNING, "Error retrieving 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 Hypervisor {0} as {1}/******");
            rec.setParameters(new Object[]{hypervisorHost, username});
            LOGGER.log(rec);
        }

        return domains;
    }

    /**
     * Returns a <code>List</code> of VMs configured on the hypervisor. This method always retrieves the current list of
     * VMs to ensure that newly available instances show up right away.
     *
     * @return the virtual machines
     */
    public synchronized List<VirtualMachine> getVirtualMachines() {
      List<VirtualMachine> vmList = new ArrayList<VirtualMachine>();
        try {
          Map<String, IDomain> 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;
    }

    /**
     * Returns an array of snapshots names/ids of a given VM as found by libvirt.
     *
     * @param virtualMachineName   the name of the vm
     * @return             the array of snapshot ids (can be empty)
     */
    public synchronized String[] getSnapshots (String virtualMachineName) {
      try {
          for (IDomain domain : getDomains().values()) {
            if (domain.getName().equals(virtualMachineName)) {
              LogRecord rec = new LogRecord(Level.FINE, "Fetching snapshots for " + virtualMachineName + ": " + domain.snapshotNum());
              LOGGER.log(rec);
              return domain.snapshotListNames();
            }
          }
      } catch (VirtException lve) {
        LogRecord rec = new LogRecord(Level.SEVERE, "Failed to fetch snapshot ids for VM {0} at datacenter {1} as {2}/******");
            rec.setThrown(lve);
            rec.setParameters(new Object[]{virtualMachineName, hypervisorHost, username});
            LOGGER.log(rec);
      }
        return new String[0];
    }

    public Collection<NodeProvisioner.PlannedNode> provision(Label label, int i) {
        return Collections.emptySet();
    }

    public boolean canProvision(Label label) {
        return false;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Hypervisor");
        sb.append("{hypervisorUri='").append(hypervisorHost).append('\'');
        sb.append(", username='").append(username).append('\'');
        sb.append('}');
        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) throws VirtException {
        ensureLists();
       
        if (currentOnline.remove(slaveName) != null)
            currentOnlineSlaveCount--;
    }

    @Override
  protected void finalize() throws Throwable {
    if (connection != null)
      connection.close();
    super.finalize();
  }

  @Override
    public DescriptorImpl getDescriptor() {
        return (DescriptorImpl) super.getDescriptor();
    }

    public static StandardUsernameCredentials lookupSystemCredentials(String credentialsId) {
        if(Strings.isNullOrEmpty(credentialsId))
            return null;
        return CredentialsMatchers.firstOrNull(
                CredentialsProvider
                        .lookupCredentials(StandardUsernameCredentials.class, Jenkins.getInstance(), ACL.SYSTEM,
                                new SchemeRequirement("ssh")),
                CredentialsMatchers.withId(credentialsId)
        );
    }

    public String getHypervisorURI() {
        return createBuilder().constructHypervisorURI();
    }

    @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 ListBoxModel doFillCredentialsIdItems(@AncestorInPath ItemGroup context) {

            return new SSHUserListBoxModel().withMatching(SSHAuthenticator.matcher(Connection.class),
                    CredentialsProvider.lookupCredentials(StandardUsernameCredentials.class, context,
                            ACL.SYSTEM, new SchemeRequirement("ssh")));
        }

        public FormValidation doTestConnection(
                @QueryParameter String hypervisorType, @QueryParameter String hypervisorHost, @QueryParameter String hypervisorSshPort,
                @QueryParameter String username, @QueryParameter String hypervisorSystemUrl,
                @QueryParameter boolean useNativeJavaConnection, @QueryParameter String credentialsId) 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!");
                }

                ConnectionBuilder builder = ConnectionBuilder.newBuilder()
                        .hypervisorType(hypervisorType)
                        .userName(username)
                        .withCredentials(lookupSystemCredentials(credentialsId))
                        .hypervisorHost(hypervisorHost)
                        .hypervisorPort(Integer.parseInt(hypervisorSshPort))
                        .hypervisorSysUrl(hypervisorSystemUrl)
                        .useNativeJava(useNativeJavaConnection);

                String hypervisorUri = builder.constructHypervisorURI();

                LogRecord rec = new LogRecord(Level.FINE, "Testing connection to hypervisor: {0}");
                rec.setParameters(new Object[]{hypervisorUri});
                LOGGER.log(rec);
               
                IConnect hypervisorConnection = builder.build();
                long version = hypervisorConnection.getVersion();
                hypervisorConnection.close();
                return FormValidation.ok("OK: " + hypervisorUri + ", version=" + version);
            } catch (VirtException 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 jenkins machine!");
                rec.setThrown(e);
                rec.setParameters(new Object[]{hypervisorHost, username});
                LOGGER.log(rec);
                return FormValidation.error(e.getMessage());
            } catch (Exception e) {
                LogRecord rec = new LogRecord(Level.WARNING, "Failed to connect to hypervisor. Check libvirt installation on jenkins 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");
            types.add("LXC");
            types.add("BHYVE");
            return types;
        }
    }
}
TOP

Related Classes of hudson.plugins.libvirt.Hypervisor$DescriptorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.