// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by 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. // // Automatically generated by addcopyright.py at 04/03/2012 * follows: * Linking this library statically or dynamically with other modules is * making a combined work based on this library. Thus, the terms and * permission to link this library with independent modules to produce an * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. package com.xensource.xenapi; import java.net.URL; import java.util.Map; import java.util.TimeZone; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfig; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; import org.apache.xmlrpc.client.XmlRpcHttpClientConfig; import org.apache.xmlrpc.client.XmlRpcSun15HttpTransportFactory; import com.xensource.xenapi.Types.BadServerResponse; import com.xensource.xenapi.Types.SessionAuthenticationFailed; import com.xensource.xenapi.Types.XenAPIException; /** * Represents a connection to a XenServer. Creating a new instance of this class initialises a new XmlRpcClient that is * then used by all method calls: each method call in xenapi takes a Connection as a parameter, composes an XMLRPC * method call, and dispatches it on the Connection's client via the dispatch method. */ public class Connection { /** * The version of the bindings that this class belongs to. */ public static final String BINDINGS_VERSION = "5.6.100-1"; /** * true if the connection is to the Rio edition of XenServer. Certain function calls are not allowed. * * @deprecated Use getAPIVersion() instead. */ @Deprecated public Boolean rioConnection = false; private APIVersion apiVersion; protected int _wait = 600; /** * Updated when Session.login_with_password() is called. */ public APIVersion getAPIVersion() { return apiVersion; } /** * The opaque reference to the session used by this connection */ private String sessionReference; /** * As seen by the xmlrpc library. From our point of view it's a server. */ private final XmlRpcClient client; private final boolean deprecatedConstructorUsed; /** * Creates a connection to a particular server using a given username and password. This object can then be passed * in to any other API calls. * * This constructor calls Session.loginWithPassword, passing itself as the first parameter. * * When this constructor is used, a call to dispose() (also called in the Connection's finalizer) will attempt a * Session.logout on this connection. * * @deprecated Use a constructor that takes a URL as the first parameter instead. */ @Deprecated public Connection(String client, String username, String password) throws java.net.MalformedURLException, XmlRpcException, BadServerResponse, SessionAuthenticationFailed, XenAPIException { deprecatedConstructorUsed = true; // To login normally we call login_with_password(username, password, "1.X"). On rio this call fails and we // should use login_with_password(username,password) instead, and note that we are talking to a rio host so that we // can refuse to make certain miami-specific calls final String ApiVersion = APIVersion.latest().toString(); this.client = getClientFromURL(new URL(client)); try { //first try to login the modern way this.sessionReference = loginWithPassword(this.client, username, password, ApiVersion); } catch (BadServerResponse e) { //oops, something went wrong String[] errDesc = e.errorDescription; //was the problem that the host was running rio? If so it will have complained that it got three parameters //instead of two. Let us carefully verify the details of this complaint if (0 == errDesc[0].compareTo("MESSAGE_PARAMETER_COUNT_MISMATCH") && 0 == errDesc[1].compareTo("session.login_with_password") && 0 == errDesc[2].compareTo("2") && 0 == errDesc[3].compareTo("3")) { //and if so, we can have another go, using the older login method, and see how that goes. this.sessionReference = loginWithPassword(this.client, username, password); //success!. Note that we are talking to an old host on this connection this.rioConnection = true; } else { //Hmm... Can't solve this here. Let upstairs know about the problem. throw e; } } try { setAPIVersion(new Session(sessionReference)); } catch (XenAPIException exn) { dispose(); throw exn; } catch (XmlRpcException exn) { dispose(); throw exn; } } /** * Creates a connection to a particular server using a given username and password. This object can then be passed * in to any other API calls. * * Note this constructor does NOT call Session.loginWithPassword; the programmer is responsible for calling it, * passing the Connection as a parameter. No attempt to connect to the server is made until login is called. * * When this constructor is used, a call to dispose() will do nothing. The programmer is responsible for manually * logging out the Session. */ public Connection(URL url, int wait) { deprecatedConstructorUsed = false; _wait = wait; this.client = getClientFromURL(url); } /** * Creates a connection to a particular server using a given username and password. This object can then be passed * in to any other API calls. * * The additional sessionReference parameter must be a reference to a logged-in Session. Any method calls on this * Connection will use it. This constructor does not call Session.loginWithPassword, and dispose() on the resulting * Connection object does not call Session.logout. The programmer is responsible for ensuring the Session is logged * in and out correctly. */ public Connection(URL url, String sessionReference) { deprecatedConstructorUsed = false; this.client = getClientFromURL(url); this.sessionReference = sessionReference; } protected void finalize() throws Throwable { dispose(); super.finalize(); } /** * Nothrow guarantee. */ public void dispose() { if (!deprecatedConstructorUsed) { // We only need to do the Session.logout if they used the old deprecated constructor. return; } try { if (sessionReference != null) { String method_call = "session.logout"; Object[] method_params = { Marshalling.toXMLRPC(this.sessionReference) }; client.execute(method_call, method_params); sessionReference = null; } } catch (XmlRpcException exn) { } } /** * @deprecated The programmer is now responsible for calling login/logout themselves. */ @Deprecated private static String loginWithPassword(XmlRpcClient client, String username, String password) throws BadServerResponse, XmlRpcException, SessionAuthenticationFailed { String method_call = "session.login_with_password"; Object[] method_params = { Marshalling.toXMLRPC(username), Marshalling.toXMLRPC(password) }; Map response = (Map) client.execute(method_call, method_params); if (response.get("Status").equals("Success")) { return (String) response.get("Value"); } else if (response.get("Status").equals("Failure")) { Object[] error = (Object[]) response.get("ErrorDescription"); if (error[0].equals("SESSION_AUTHENTICATION_FAILED")) { throw new SessionAuthenticationFailed(); } } throw new BadServerResponse(response); } /** * @deprecated The programmer is now responsible for calling login/logout themselves. */ @Deprecated private static String loginWithPassword(XmlRpcClient client, String username, String password, String ApiVersion) throws BadServerResponse, XmlRpcException, SessionAuthenticationFailed { String method_call = "session.login_with_password"; Object[] method_params = { Marshalling.toXMLRPC(username), Marshalling.toXMLRPC(password), Marshalling.toXMLRPC(ApiVersion) }; Map response = (Map) client.execute(method_call, method_params); if (response.get("Status").equals("Success")) { return (String) response.get("Value"); } else if (response.get("Status").equals("Failure")) { Object[] error = (Object[]) response.get("ErrorDescription"); if (error[0].equals("SESSION_AUTHENTICATION_FAILED")) { throw new SessionAuthenticationFailed(); } } throw new BadServerResponse(response); } private XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); public XmlRpcClientConfigImpl getConfig() { return config; } protected XmlRpcClient getClientFromURL(URL url) { config.setTimeZone(TimeZone.getTimeZone("UTC")); config.setServerURL(url); config.setReplyTimeout(_wait * 1000); config.setConnectionTimeout(5000); XmlRpcClient client = new XmlRpcClient(); client.setConfig(config); return client; } /* * Because the binding calls are constructing their own parameter lists, they need to be able to get to * the session reference directly. This is all rather ugly and needs redone * Changed to public to allow easier integration with HTTP-level streaming interface, * see CA-15447 */ public String getSessionReference() { return this.sessionReference; } /** * The (auto-generated parts of) the bindings dispatch XMLRPC calls on this Connection's client through this method. */ protected Map dispatch(String method_call, Object[] method_params) throws XmlRpcException, XenAPIException { Map response = (Map) client.execute(method_call, method_params); if (!deprecatedConstructorUsed) { // We are using the new-style constructor which doesn't perform login. // Set this Connection's Session reference from the value returned on the wire. if (method_call.equals("session.login_with_password") && response.get("Status").equals("Success")) { // Store the Session reference and ask the server what the // API version it's using is. Session session = Types.toSession(response.get("Value")); sessionReference = session.ref; setAPIVersion(session); } else if (method_call.equals("session.slave_local_login_with_password") && response.get("Status").equals("Success")) { // Store the Session reference and assume API version 1.2. sessionReference = Types.toSession(response.get("Value")).ref; apiVersion = APIVersion.API_1_2; } else if (method_call.equals("session.logout")) { // Work around a bug in XenServer 5.0 and below. // session.login_with_password should have rejected us with // HOST_IS_SLAVE, but instead we don't find out until later. // We don't want to leak the session, so we need to log out // this session from the master instead. if (response.get("Status").equals("Failure")) { Object[] error = (Object[]) response.get("ErrorDescription"); if (error.length == 2 && error[0].equals("HOST_IS_SLAVE")) { try { URL client_url = ((XmlRpcHttpClientConfig)client.getClientConfig()).getServerURL(); Connection tmp_conn = new Connection(new URL(client_url.getProtocol(), (String)error[1], client_url.getPort(), client_url.getFile()), _wait); tmp_conn.sessionReference = sessionReference; try { Session.logout(tmp_conn); } finally { tmp_conn.dispose(); } } catch (Exception exn2) { // Ignore -- we're going to throw HostIsSlave anyway. } } } // Clear the stored Session reference. this.sessionReference = null; } } return Types.checkResponse(response); } private void setAPIVersion(Session session) throws XenAPIException, XmlRpcException { try { long major = session.getThisHost(this).getAPIVersionMajor(this); long minor = session.getThisHost(this).getAPIVersionMinor(this); apiVersion = APIVersion.fromMajorMinor(major, minor); } catch (BadServerResponse exn) { apiVersion = APIVersion.API_1_1; } } }