http://git-wip-us.apache.org/repos/asf/cloudstack/blob/748b575a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerConnectionPool.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerConnectionPool.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerConnectionPool.java new file mode 100644 index 0000000..ae065d2 --- /dev/null +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerConnectionPool.java @@ -0,0 +1,503 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.hypervisor.xenserver.resource; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Queue; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSession; + +import org.apache.log4j.Logger; +import org.apache.xmlrpc.XmlRpcException; +import org.apache.xmlrpc.client.XmlRpcClientException; + +import com.xensource.xenapi.APIVersion; +import com.xensource.xenapi.Connection; +import com.xensource.xenapi.Host; +import com.xensource.xenapi.Pool; +import com.xensource.xenapi.Session; +import com.xensource.xenapi.Types; +import com.xensource.xenapi.Types.BadServerResponse; +import com.xensource.xenapi.Types.XenAPIException; + +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.exception.CloudRuntimeException; + +public class XenServerConnectionPool { + private static final Logger s_logger = Logger.getLogger(XenServerConnectionPool.class); + protected HashMap<String /* poolUuid */, XenServerConnection> _conns = new HashMap<String, XenServerConnection>(); + protected int _retries; + protected int _interval; + protected int _connWait = 5; + protected static long s_sleepOnError = 10 * 1000; // in ms + static { + File file = PropertiesUtil.findConfigFile("environment.properties"); + if (file == null) { + s_logger.debug("Unable to find environment.properties"); + } else { + FileInputStream finputstream; + try { + finputstream = new FileInputStream(file); + final Properties props = new Properties(); + props.load(finputstream); + finputstream.close(); + String search = props.getProperty("sleep.interval.on.error"); + if (search != null) { + s_sleepOnError = NumbersUtil.parseInterval(search, 10) * 1000; + } + s_logger.info("XenServer Connection Pool Configs: sleep.interval.on.error=" + s_sleepOnError); + } catch (FileNotFoundException e) { + s_logger.debug("File is not found", e); + } catch (IOException e) { + s_logger.debug("IO Exception while reading file", e); + } + } + try { + javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1]; + javax.net.ssl.TrustManager tm = new TrustAllManager(); + trustAllCerts[0] = tm; + javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, null); + javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + HostnameVerifier hv = new HostnameVerifier() { + @Override + public boolean verify(String hostName, SSLSession session) { + return true; + } + }; + HttpsURLConnection.setDefaultHostnameVerifier(hv); + } catch (Exception e) { + } + } + + protected XenServerConnectionPool() { + _retries = 1; + _interval = 3; + } + + private void addConnect(String poolUuid, XenServerConnection conn) { + if (poolUuid == null) + return; + if (s_logger.isDebugEnabled()) { + s_logger.debug("Add master connection through " + conn.getIp() + " for pool(" + conn.getPoolUuid() + ")"); + } + synchronized (_conns) { + _conns.put(poolUuid, conn); + } + } + + private XenServerConnection getConnect(String poolUuid) { + if (poolUuid == null) + return null; + synchronized (_conns) { + return _conns.get(poolUuid); + } + } + + private void removeConnect(String poolUuid) { + if (poolUuid == null) { + return; + } + XenServerConnection conn = null; + synchronized (_conns) { + conn = _conns.remove(poolUuid); + } + if (conn != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Remove master connection through " + conn.getIp() + " for pool(" + conn.getPoolUuid() + ")"); + } + + } + } + + static void forceSleep(long sec) { + long firetime = System.currentTimeMillis() + (sec * 1000); + long msec = sec * 1000; + while (true) { + if (msec < 100) + break; + try { + Thread.sleep(msec); + return; + } catch (InterruptedException e) { + msec = firetime - System.currentTimeMillis(); + } + } + } + + public Connection getConnect(String ip, String username, Queue<String> password) { + Connection conn = new Connection(getURL(ip), 10, _connWait); + try { + loginWithPassword(conn, username, password, APIVersion.latest().toString()); + } catch (Types.HostIsSlave e) { + String maddress = e.masterIPAddress; + conn = new Connection(getURL(maddress), 10, _connWait); + try { + loginWithPassword(conn, username, password, APIVersion.latest().toString()); + } catch (Exception e1) { + String msg = "Unable to create master connection to host(" + maddress +") , due to " + e1.toString(); + s_logger.debug(msg); + throw new CloudRuntimeException(msg, e1); + } + } catch (Exception e) { + String msg = "Unable to create master connection to host(" + ip +") , due to " + e.toString(); + s_logger.debug(msg); + throw new CloudRuntimeException(msg, e); + } + return conn; + } + + public URL getURL(String ip) { + try { + return new URL("https://" + ip); + } catch (Exception e) { + String msg = "Unable to convert IP " + ip + " to URL due to " + e.toString(); + if (s_logger.isDebugEnabled()) { + s_logger.debug(msg); + } + throw new CloudRuntimeException(msg, e); + } + } + + public Connection connect(String hostUuid, String poolUuid, String ipAddress, + String username, Queue<String> password, int wait) { + XenServerConnection mConn = null; + if (hostUuid == null || poolUuid == null || ipAddress == null || username == null || password == null) { + String msg = "Connect some parameter are null hostUuid:" + hostUuid + " ,poolUuid:" + poolUuid + + " ,ipAddress:" + ipAddress; + s_logger.debug(msg); + throw new CloudRuntimeException(msg); + } + synchronized (poolUuid.intern()) { + mConn = getConnect(poolUuid); + if (mConn != null){ + try{ + Host host = Host.getByUuid(mConn, hostUuid); + if (!host.getEnabled(mConn)) { + String msg = "Cannot connect this host " + ipAddress + " due to the host is not enabled"; + s_logger.debug(msg); + if (mConn.getIp().equalsIgnoreCase(ipAddress)) { + removeConnect(poolUuid); + mConn = null; + } + throw new CloudRuntimeException(msg); + } + return mConn; + } catch (CloudRuntimeException e) { + throw e; + } catch (Exception e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("connect through IP(" + mConn.getIp() + " for pool(" + poolUuid + ") is broken due to " + e.toString()); + } + removeConnect(poolUuid); + mConn = null; + } + } + + if ( mConn == null ) { + try { + Connection conn = new Connection(getURL(ipAddress), 5, _connWait); + Session sess = loginWithPassword(conn, username, password, APIVersion.latest().toString()); + Host host = sess.getThisHost(conn); + Boolean hostenabled = host.getEnabled(conn); + if( sess != null ){ + try{ + Session.logout(conn); + } catch (Exception e) { + } + conn.dispose(); + } + if (!hostenabled) { + String msg = "Unable to create master connection, due to master Host " + ipAddress + " is not enabled"; + s_logger.debug(msg); + throw new CloudRuntimeException(msg); + } + mConn = new XenServerConnection(getURL(ipAddress), ipAddress, username, password, _retries, _interval, wait, _connWait); + loginWithPassword(mConn, username, password, APIVersion.latest().toString()); + } catch (Types.HostIsSlave e) { + String maddress = e.masterIPAddress; + mConn = new XenServerConnection(getURL(maddress), maddress, username, password, _retries, _interval, wait, _connWait); + try { + Session session = loginWithPassword(mConn, username, password, APIVersion.latest().toString()); + Host host = session.getThisHost(mConn); + if (!host.getEnabled(mConn)) { + String msg = "Unable to create master connection, due to master Host " + maddress + " is not enabled"; + s_logger.debug(msg); + throw new CloudRuntimeException(msg); + } + } catch (Exception e1) { + String msg = "Unable to create master connection to host(" + maddress +") , due to " + e1.toString(); + s_logger.debug(msg); + throw new CloudRuntimeException(msg, e1); + + } + } catch (CloudRuntimeException e) { + throw e; + } catch (Exception e) { + String msg = "Unable to create master connection to host(" + ipAddress +") , due to " + e.toString(); + s_logger.debug(msg); + throw new CloudRuntimeException(msg, e); + } + addConnect(poolUuid, mConn); + } + } + return mConn; + } + + + + protected Session slaveLocalLoginWithPassword(Connection conn, String username, Queue<String> password) throws BadServerResponse, XenAPIException, XmlRpcException { + Session s = null; + boolean logged_in = false; + Exception ex = null; + while (!logged_in) { + try { + s = Session.slaveLocalLoginWithPassword(conn, username, password.peek()); + logged_in = true; + } catch (BadServerResponse e) { + logged_in = false; + ex = e; + } catch (XenAPIException e) { + logged_in = false; + ex = e; + } catch (XmlRpcException e) { + logged_in = false; + ex = e; + } + if (logged_in && conn != null) { + break; + } else { + if (password.size() > 1) { + password.remove(); + continue; + } else { + // the last password did not work leave it and flag error + if (ex instanceof BadServerResponse) { + throw (BadServerResponse)ex; + } else if (ex instanceof XmlRpcException) { + throw (XmlRpcException)ex; + } else if (ex instanceof Types.SessionAuthenticationFailed) { + throw (Types.SessionAuthenticationFailed)ex; + } else if (ex instanceof XenAPIException) { + throw (XenAPIException)ex; + } + break; + } + } + } + return s; + } + + protected Session loginWithPassword(Connection conn, String username, Queue<String> password, String version) throws BadServerResponse, XenAPIException, + XmlRpcException { + Session s = null; + boolean logged_in = false; + Exception ex = null; + while (!logged_in) { + try { + s = Session.loginWithPassword(conn, username, password.peek(), APIVersion.latest().toString()); + logged_in = true; + } catch (BadServerResponse e) { + logged_in = false; + ex = e; + } catch (XenAPIException e) { + logged_in = false; + ex = e; + } catch (XmlRpcException e) { + logged_in = false; + ex = e; + } + + if (logged_in && conn != null) { + break; + } else { + if (password.size() > 1) { + password.remove(); + continue; + } else { + // the last password did not work leave it and flag error + if (ex instanceof BadServerResponse) { + throw (BadServerResponse)ex; + } else if (ex instanceof XmlRpcException) { + throw (XmlRpcException)ex; + } else if (ex instanceof Types.SessionAuthenticationFailed) { + throw (Types.SessionAuthenticationFailed)ex; + } else if (ex instanceof XenAPIException) { + throw (XenAPIException)ex; + } + } + } + } + return s; + } + + protected void join(Connection conn, String masterIp, String username, Queue<String> password) throws BadServerResponse, XenAPIException, XmlRpcException, + Types.JoiningHostCannotContainSharedSrs { + + boolean logged_in = false; + Exception ex = null; + while (!logged_in) { + try { + Pool.join(conn, masterIp, username, password.peek()); + logged_in = true; + } catch (BadServerResponse e) { + logged_in = false; + ex = e; + } catch (XenAPIException e) { + logged_in = false; + ex = e; + } catch (XmlRpcException e) { + logged_in = false; + ex = e; + } + if (logged_in && conn != null) { + break; + } else { + if (password.size() > 1) { + password.remove(); + continue; + } else { + // the last password did not work leave it and flag error + if (ex instanceof BadServerResponse) { + throw (BadServerResponse)ex; + } else if (ex instanceof XmlRpcException) { + throw (XmlRpcException)ex; + } else if (ex instanceof Types.SessionAuthenticationFailed) { + throw (Types.SessionAuthenticationFailed)ex; + } else if (ex instanceof XenAPIException) { + throw (XenAPIException)ex; + } + break; + } + } + } + } + + static public Pool.Record getPoolRecord(Connection conn) throws XmlRpcException, XenAPIException { + Map<Pool, Pool.Record> pools = Pool.getAllRecords(conn); + assert pools.size() == 1 : "Pool size is not one....hmmm....wth? " + pools.size(); + + return pools.values().iterator().next(); + } + + private static final XenServerConnectionPool s_instance = new XenServerConnectionPool(); + + public static XenServerConnectionPool getInstance() { + return s_instance; + } + + public class XenServerConnection extends Connection { + long _interval; + int _retries; + String _ip; + String _username; + Queue<String> _password; + String _poolUuid; + + public XenServerConnection(URL url, String ip, String username, Queue<String> password, int retries, int interval, int wait, int connwait) { + super(url, wait, connwait); + _ip = ip; + _retries = retries; + _username = username; + _password = password; + _interval = (long)interval * 1000; + + } + + public String getPoolUuid() { + return _poolUuid; + } + + public String getUsername() { + return _username; + } + + public Queue<String> getPassword() { + return _password; + } + + public String getIp() { + return _ip; + } + + @Override + protected Map dispatch(String methodcall, Object[] methodparams) throws XmlRpcException, XenAPIException { + if (methodcall.equals("session.local_logout") + || methodcall.equals("session.slave_local_login_with_password") + || methodcall.equals("session.logout") + || methodcall.equals("session.login_with_password")) { + return super.dispatch(methodcall, methodparams); + } + + try { + return super.dispatch(methodcall, methodparams); + } catch (Types.SessionInvalid e) { + s_logger.debug("Session is invalid for method: " + methodcall + " due to " + e.toString()); + removeConnect(_poolUuid); + throw e; + } catch (XmlRpcClientException e) { + s_logger.debug("XmlRpcClientException for method: " + methodcall + " due to " + e.toString()); + removeConnect(_poolUuid); + throw e; + } catch (XmlRpcException e) { + s_logger.debug("XmlRpcException for method: " + methodcall + " due to " + e.toString()); + removeConnect(_poolUuid); + throw e; + } catch (Types.HostIsSlave e) { + s_logger.debug("HostIsSlave Exception for method: " + methodcall + " due to " + e.toString()); + removeConnect(_poolUuid); + throw e; + } + } + } + + public static class TrustAllManager implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager { + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + + public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) { + return true; + } + + public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) { + return true; + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { + return; + } + + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { + return; + } + } + +}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/748b575a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerPoolVms.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerPoolVms.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerPoolVms.java new file mode 100644 index 0000000..8042209 --- /dev/null +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerPoolVms.java @@ -0,0 +1,90 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.hypervisor.xenserver.resource; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.log4j.Logger; + +import com.cloud.utils.Pair; +import com.cloud.vm.VirtualMachine.State; + +public class XenServerPoolVms { + private static final Logger s_logger = Logger.getLogger(XenServerPoolVms.class); + private final Map<String/* clusterId */, HashMap<String/* vm name */, Pair<String/* host uuid */, State/* vm state */>>> _clusterVms = + new ConcurrentHashMap<String, HashMap<String, Pair<String, State>>>(); + + public HashMap<String, Pair<String, State>> getClusterVmState(String clusterId) { + HashMap<String, Pair<String, State>> _vms = _clusterVms.get(clusterId); + if (_vms == null) { + HashMap<String, Pair<String, State>> vmStates = new HashMap<String, Pair<String, State>>(); + _clusterVms.put(clusterId, vmStates); + return vmStates; + } else + return _vms; + } + + public void clear(String clusterId) { + HashMap<String, Pair<String, State>> _vms = getClusterVmState(clusterId); + _vms.clear(); + } + + public State getState(String clusterId, String name) { + HashMap<String, Pair<String, State>> vms = getClusterVmState(clusterId); + Pair<String, State> pv = vms.get(name); + return pv == null ? State.Stopped : pv.second(); // if a VM is absent on the cluster, it is effectively in stopped state. + } + + public Pair<String, State> get(String clusterId, String name) { + HashMap<String, Pair<String, State>> vms = getClusterVmState(clusterId); + return vms.get(name); + } + + public void put(String clusterId, String hostUuid, String name, State state) { + HashMap<String, Pair<String, State>> vms = getClusterVmState(clusterId); + vms.put(name, new Pair<String, State>(hostUuid, state)); + } + + public void remove(String clusterId, String hostUuid, String name) { + HashMap<String, Pair<String, State>> vms = getClusterVmState(clusterId); + vms.remove(name); + } + + public void putAll(String clusterId, HashMap<String, Pair<String, State>> newVms) { + HashMap<String, Pair<String, State>> vms = getClusterVmState(clusterId); + vms.putAll(newVms); + } + + public int size(String clusterId) { + HashMap<String, Pair<String, State>> vms = getClusterVmState(clusterId); + return vms.size(); + } + + @Override + public String toString() { + StringBuilder sbuf = new StringBuilder("PoolVms="); + for (HashMap<String/* vm name */, Pair<String/* host uuid */, State/* vm state */>> clusterVM : _clusterVms.values()) { + for (String vmname : clusterVM.keySet()) { + sbuf.append(vmname).append("-").append(clusterVM.get(vmname).second()).append(","); + } + } + return sbuf.toString(); + } + +}
