/** * Abiquo community edition * cloud management application for hybrid clouds * Copyright (C) 2008-2010 - Abiquo Holdings S.L. * * This application is free software; you can redistribute it and/or * modify it under the terms of the GNU LESSER GENERAL PUBLIC * LICENSE as published by the Free Software Foundation under * version 3 of the License * * This software 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 * LESSER GENERAL PUBLIC LICENSE v.3 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package com.abiquo.nodecollector.client; import java.net.SocketTimeoutException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response.Status; import org.apache.commons.httpclient.URIException; import org.apache.commons.httpclient.util.URIUtil; import org.apache.wink.client.ClientConfig; import org.apache.wink.client.ClientResponse; import org.apache.wink.client.ClientRuntimeException; import org.apache.wink.client.Resource; import org.apache.wink.client.RestClient; import org.apache.wink.common.internal.utils.UriHelper; import com.abiquo.model.enumerator.HypervisorType; import com.abiquo.model.transport.error.ErrorDto; import com.abiquo.nodecollector.domain.HypervisorCollector; import com.abiquo.nodecollector.exception.BadRequestException; import com.abiquo.nodecollector.exception.CannotExecuteException; import com.abiquo.nodecollector.exception.CollectorException; import com.abiquo.nodecollector.exception.ConnectionException; import com.abiquo.nodecollector.exception.LoginException; import com.abiquo.nodecollector.exception.NoManagedException; import com.abiquo.nodecollector.exception.ServiceUnavailableException; import com.abiquo.nodecollector.exception.UnprovisionedException; import com.abiquo.server.core.infrastructure.nodecollector.HostDto; import com.abiquo.server.core.infrastructure.nodecollector.HostStatusEnumType; import com.abiquo.server.core.infrastructure.nodecollector.HypervisorEnumTypeDto; import com.abiquo.server.core.infrastructure.nodecollector.VirtualSystemCollectionDto; import com.abiquo.server.core.infrastructure.nodecollector.VirtualSystemDto; /** * An abstract layer to all the calls to the nodecollector. * * @author jdevesa@abiquo.com */ public class NodeCollectorRESTClient { /** * Encapsulated Wink Client REST object. */ protected RestClient client; // Paths of the resource /** * Path Value to the {@link HypervisorEnumTypeDto} resource. */ protected static String hypervisorPath = "hypervisor"; /** * Path value to the {@link HostDto} resource. */ protected static String hostPath = "host"; /** * Path value to the {@link VirtualSystemDto} resource. */ protected static String virtualSystemPath = "virtualsystem"; /** * Path value to filter a virtual system by its UUID. */ protected static String byUUIDPath = "by_uuid"; /** * Path value to filter a virtual system by its Name. */ protected static String byNamePath = "by_name"; // Query parameters. /** * Query parameter key for the Hypervisor. */ protected static String hypervisorKey = "hyp"; /** * Query parameter key for the User. */ protected static String userKey = "user"; /** * Query parameter key for the password. */ protected static String passwordKey = "passwd"; /** * The CHECK uri */ public static final String checkResource = "check"; /** * Query parameter key for the aim port. */ public static final String AIMPORT = "aimport"; // Definition of the hypervisor specific values. /** * URI of the remote service */ protected String remoteServiceURI; /** * Standard message error for unreachable discovery manager. */ protected static final String UNREACHABLE = "Discovery Manager unreachable. "; /** * Standard message for timeout reached */ protected static final String TIMEOUT = "Time out has reached, cannot access the machine. "; /** STONITH URI CONSTANTS */ protected static final String STONITH_PATH = "stonith"; protected static final String BELONG_TO_UCS_PARAM = "stonith"; protected static final String STONITH_UP_PATH = "up"; protected static final String HOST_PARAM = "host"; protected static final String PORT_PARAM = "port"; protected static final String USERNAME_PARAM = "user"; protected static final String PASSWORD_PARAM = "password"; /** * Constructor of the REST client. * * @param remoteServiceURI IP Address where the NodeCollector is deployed. */ public NodeCollectorRESTClient(final String remoteServiceURI) { ClientConfig c = new ClientConfig(); c.readTimeout(Integer.parseInt(System.getProperty("abiquo.nodecollector.timeout", "0"))); c.connectTimeout(Integer.parseInt(System.getProperty("abiquo.nodecollector.timeout", "0"))); client = new RestClient(c); this.remoteServiceURI = remoteServiceURI; } /** * Construct of the REST client with the timeouts * * @param remoteServiceURI IP Address where the NodeCollector is deployed. * @param readTimeout timeout of the connection in reading response. * @param connectTimeout timout in connection-time. */ public NodeCollectorRESTClient(final String remoteServiceURI, final Integer readTimeout, final Integer connectTimeout) { ClientConfig c = new ClientConfig(); c.readTimeout(readTimeout == null ? 0 : readTimeout); c.connectTimeout(connectTimeout == null ? 0 : connectTimeout); client = new RestClient(c); this.remoteServiceURI = remoteServiceURI; } /** * Return the Hypervisor running in the given IP. * * @param hypervisorIP IP which we want to collect is Hypervisor. * @return {@link HypervisorEnumTypeDto} object. * @throws BadRequestException if any of the parameters is wrong or missed. * @throws ConnectionException if any machine responds in the given IP address. * @throws UnprovisionedException if there is a machine in the given IP, but it has not any * Hypervisor running. * @throws CollectorException for unexpected exceptions. * @throws ServiceUnavailableException * @throws CannotExecuteException * @throws NoManagedException */ public HypervisorType getRemoteHypervisorType(final String hypervisorIP) throws BadRequestException, ConnectionException, UnprovisionedException, CollectorException, ServiceUnavailableException, CannotExecuteException, NoManagedException { String uri = appendPathToBaseUri(remoteServiceURI, hypervisorIP, hypervisorPath); try { Resource resource = client.resource(uri); ClientResponse response = resource.accept(MediaType.APPLICATION_XML_TYPE).get(); if (response.getStatusCode() != Status.OK.getStatusCode()) { try { throwAppropiateException(response); } catch (LoginException e) { // It will never happen, so we must encapsulate in the non-expected exceptions. throw new CollectorException(e.getMessage(), e); } } return HypervisorType.fromValue(response.getEntity(String.class)); } catch (ClientRuntimeException e) { if (e.getCause().getCause() instanceof SocketTimeoutException) { throw new ServiceUnavailableException(NodeCollectorRESTClient.TIMEOUT); } // Mostly caused by ConnectException throw new ServiceUnavailableException(NodeCollectorRESTClient.UNREACHABLE); } } /** * Get the Host information for a remote machine. * * @param hypervisorIP IP of the remote machine. * @param hypervisorType {@link HypervisorEnumTypeDto} object containgin Hypervisor is running * remotely. * @param user user to login to the Hypervisor. * @param password password to authenticate to the Hypervisor. * @param aimport port of the aim. * @param repositoryLocation this parameter is for define the {@link HostStatusEnumType}. * Corresponding {@link HypervisorCollector} implementation will check if there is * any Datastore mounted in the remote machine which matches withe this parameter. * @return the {@link HostDto} object with the machine information. * @throws BadRequestException if any parameter is missing, wrong or null. * @throws LoginException if the provided user and password don't match with any Hypervisor * user. * @throws ConnectionException if the remote machine doesn't run the provided hypervisorType * parameter. * @throws UnprovisionedException if the machine doesn't respond. * @throws CollectorException for unexpected exceptions. * @throws ServiceUnavailableException * @throws CannotExecuteException * @throws NoManagedException */ public HostDto getRemoteHostInfo(final String hypervisorIP, final HypervisorType hypervisorType, final String user, final String password, final Integer aimport) throws BadRequestException, LoginException, ConnectionException, UnprovisionedException, CollectorException, ServiceUnavailableException, CannotExecuteException, NoManagedException { String uri = appendPathToBaseUri(remoteServiceURI, hypervisorIP, hostPath); Resource resource = client.resource(uri).queryParam(hypervisorKey, hypervisorType.getValue()) .queryParam(userKey, user).queryParam(passwordKey, password); if (aimport != null) { resource.queryParam(AIMPORT, aimport); } try { ClientResponse response = resource.accept(MediaType.APPLICATION_XML_TYPE).get(); if (response.getStatusCode() != Status.OK.getStatusCode()) { throwAppropiateException(response); } return response.getEntity(HostDto.class); } catch (ClientRuntimeException e) { if (e.getCause().getCause() instanceof SocketTimeoutException) { throw new ServiceUnavailableException(NodeCollectorRESTClient.TIMEOUT); } // Mostly caused by ConnectException throw new ServiceUnavailableException(NodeCollectorRESTClient.UNREACHABLE); } } /** * Retrieve the list of Virtual Machines deployed in the remote Hypervisor. * * @param hypervisorIP IP address of the remote machine. * @param hypervisorType {@link HypervisorEnumTypeDto} object containgin Hypervisor is running * remotely. * @param user user to login to the Hypervisor. * @param password password to authenticate to the Hypervisor. * @param aimport port of the aim. * @return the list of Virtual Machines encapsulated inside the * {@link VirtualSystemCollectionDto} object. * @throws BadRequestException if any parameter is missing, wrong or null. * @throws LoginException if the provided user and password don't match with any Hypervisor * user. * @throws ConnectionException if the remote machine doesn't run the provided hypervisorType * parameter. * @throws UnprovisionedException if the machine doesn't respond. * @throws CollectorException for unexpected exceptions. * @throws CannotExecuteException * @throws NoManagedException */ public VirtualSystemCollectionDto getRemoteVirtualSystemCollection(final String hypervisorIP, final HypervisorType hypervisorType, final String user, final String password, final Integer aimport) throws BadRequestException, LoginException, ConnectionException, UnprovisionedException, CollectorException, CannotExecuteException, NoManagedException { String uri = appendPathToBaseUri(remoteServiceURI, hypervisorIP, virtualSystemPath); Resource resource = client.resource(uri).queryParam(hypervisorKey, hypervisorType.getValue()) .queryParam(userKey, user).queryParam(passwordKey, password); if (aimport != null) { resource.queryParam(AIMPORT, aimport); } try { ClientResponse response = resource.accept(MediaType.APPLICATION_XML_TYPE).get(); if (response.getStatusCode() != Status.OK.getStatusCode()) { throwAppropiateException(response); } return response.getEntity(VirtualSystemCollectionDto.class); } catch (ClientRuntimeException e) { if (e.getCause().getCause() instanceof SocketTimeoutException) { throw new ConnectionException(NodeCollectorRESTClient.TIMEOUT); } // Mostly caused by ConnectException throw new ConnectionException(NodeCollectorRESTClient.UNREACHABLE); } } public boolean isStonithUp(final String ip, final Integer port, final String username, final String password) throws ConnectionException, BadRequestException { try { String uri = appendPathToBaseUri(remoteServiceURI, STONITH_PATH, STONITH_UP_PATH); Resource resource = client.resource(uri); resource.queryParam(HOST_PARAM, ip); resource.queryParam(USERNAME_PARAM, username); resource.queryParam(PASSWORD_PARAM, password); if (port != null) { resource.queryParam(PORT_PARAM, port.toString()); } ClientResponse response = resource.accept(MediaType.APPLICATION_XML_TYPE).get(); if (response.getStatusCode() != Status.NO_CONTENT.getStatusCode()) { return false; } return true; } catch (ClientRuntimeException e) { return false; } } public boolean stonithNode(final String ip, final Integer port, final String username, final String password, final String bladeDn) { try { String uri = appendPathToBaseUri(remoteServiceURI, STONITH_PATH); Resource resource = client.resource(uri); resource.queryParam(HOST_PARAM, ip); resource.queryParam(USERNAME_PARAM, username); resource.queryParam(PASSWORD_PARAM, password); if (port != null) { resource.queryParam(PORT_PARAM, port.toString()); } if (bladeDn != null) { resource.queryParam(BELONG_TO_UCS_PARAM, bladeDn); } ClientResponse response = resource.accept(MediaType.APPLICATION_XML_TYPE).post(null); if (response.getStatusCode() != Status.NO_CONTENT.getStatusCode()) { return false; } return true; } catch (ClientRuntimeException e) { return false; } } public boolean stonithNode(final String ip, final Integer port, final String username, final String password) { return stonithNode(ip, port, username, password, null); } /** * Helper method to convert the HTTP response codes to Java exceptions. * * @param error error object we get. * @throws BadRequestException is thrown if we get a 400 Bad Request error response. * @throws LoginException is thrown if we get a 401 Unauthorized error response. * @throws ConnectionException is thrown if we get a 412 Precondition Failed error response. * @throws UnprovisionedException is thrown if we get a 404 Not Found error response. * @throws CollectorException is thrown if we get another exception, mostly the 500 Internal * Server Error. * @throws CannotExecuteException * @throws NoManagedException */ protected void throwAppropiateException(final ClientResponse response) throws BadRequestException, LoginException, ConnectionException, UnprovisionedException, CollectorException, CannotExecuteException, NoManagedException { ErrorDto error; if (response.getStatusCode() == Status.INTERNAL_SERVER_ERROR.getStatusCode()) { throw new CollectorException("Discovery Manager " + response.getMessage()); } // if we get a 404 Not-found or a 400 Bad Request in a running nodecollector the // ErrorResponse would be // deserialized. // If we can not deserialize it means there is a running web-server but without the // nodecollector. (404 for web servers with ROOT context, 400 for web servers without ROOT // context) try { error = response.getEntity(ErrorDto.class); } catch (Exception e) { // Mostly caused by ConnectException throw new ConnectionException(NodeCollectorRESTClient.UNREACHABLE); } // In case of errors, we only serialize the first error. switch (response.getStatusCode()) { case 400: throw new BadRequestException(error.getMessage()); case 401: throw new LoginException(error.getMessage()); case 404: throw new UnprovisionedException(error.getMessage()); case 409: throw new CannotExecuteException(error.getMessage()); case 412: throw new ConnectionException(error.getMessage()); case 406: throw new NoManagedException(error.getMessage()); default: throw new CollectorException(error.getMessage()); } } /** * @param uri * @param paths * @return */ protected String appendPathToBaseUri(String uri, final String... paths) { for (String path : paths) { uri = UriHelper.appendPathToBaseUri(uri, path); } return uri; } public String getRemoteServiceURI() { return remoteServiceURI; } /** * Get a unique and known remote Virtual Machine information based on its UUID. * * @param uuid identifier of the remote virtual machine. * @param hypervisorIP IP address of the remote machine. * @param hypervisorType {@link HypervisorEnumTypeDto} object containgin Hypervisor is running * remotely. * @param user user to login to the Hypervisor. * @param password password to authenticate to the Hypervisor. * @param aimport port of the aim * @return the Virtual Machine information encapsulated into the {@link VirtualSystemDto} * object. * @throws BadRequestException if any parameter is missing, wrong or null. * @throws LoginException if the provided user and password don't match with any Hypervisor * user. * @throws ConnectionException if the remote machine doesn't run the provided hypervisorType * parameter. * @throws UnprovisionedException if the machine doesn't respond. * @throws CollectorException for unexpected exceptions. * @throws CannotExecuteException * @throws NoManagedException */ public VirtualSystemDto getRemoteVirtualSystemByUUID(final String uuid, final String hypervisorIP, final HypervisorType hypervisorType, final String user, final String password, final Integer aimport) throws BadRequestException, LoginException, ConnectionException, UnprovisionedException, CollectorException, CannotExecuteException, NoManagedException { String uri = appendPathToBaseUri(remoteServiceURI, hypervisorIP, virtualSystemPath, byUUIDPath, uuid); Resource resource = client.resource(uri).queryParam(hypervisorKey, hypervisorType.getValue()) .queryParam(userKey, user).queryParam(passwordKey, password); if (aimport != null) { resource.queryParam(AIMPORT, aimport); } try { ClientResponse response = resource.accept(MediaType.APPLICATION_XML_TYPE).get(); if (response.getStatusCode() != 200) { throwAppropiateException(response); } return response.getEntity(VirtualSystemDto.class); } catch (ClientRuntimeException e) { if (e.getCause().getCause() instanceof SocketTimeoutException) { throw new ConnectionException(NodeCollectorRESTClient.TIMEOUT); } // Mostly caused by ConnectException throw new ConnectionException(NodeCollectorRESTClient.UNREACHABLE); } } /** * Get a unique and known remote Virtual Machine information based on its name. * * @param name name of the remote virtual machine. * @param hypervisorIP IP address of the remote machine. * @param hypervisorType {@link HypervisorEnumTypeDto} object containgin Hypervisor is running * remotely. * @param user user to login to the Hypervisor. * @param password password to authenticate to the Hypervisor. * @param aimport port of the aim * @return the Virtual Machine information encapsulated into the {@link VirtualSystemDto} * object. * @throws BadRequestException if any parameter is missing, wrong or null. * @throws LoginException if the provided user and password don't match with any Hypervisor * user. * @throws ConnectionException if the remote machine doesn't run the provided hypervisorType * parameter. * @throws UnprovisionedException if the machine doesn't respond. * @throws CollectorException for unexpected exceptions. * @throws CannotExecuteException * @throws NoManagedException */ public VirtualSystemDto getRemoteVirtualSystemByName(final String name, final String hypervisorIP, final HypervisorType hypervisorType, final String user, final String password, final Integer aimport) throws BadRequestException, LoginException, ConnectionException, UnprovisionedException, CollectorException, CannotExecuteException, NoManagedException { // Manage the case where the virtual machine's name contains spaces to avoid problems with // the URL String encodedName = null; try { encodedName = URIUtil.encodeQuery(name); } catch (URIException ex) { throw new ConnectionException(NodeCollectorRESTClient.UNREACHABLE); } String uri = appendPathToBaseUri(remoteServiceURI, hypervisorIP, virtualSystemPath, byNamePath, encodedName); Resource resource = client.resource(uri).queryParam(hypervisorKey, hypervisorType.getValue()) .queryParam(userKey, user).queryParam(passwordKey, password); if (aimport != null) { resource.queryParam(AIMPORT, aimport); } try { ClientResponse response = resource.accept(MediaType.APPLICATION_XML_TYPE).get(); if (response.getStatusCode() != 200) { throwAppropiateException(response); } return response.getEntity(VirtualSystemDto.class); } catch (ClientRuntimeException e) { if (e.getCause().getCause() instanceof SocketTimeoutException) { throw new ConnectionException(NodeCollectorRESTClient.TIMEOUT); } // Mostly caused by ConnectException throw new ConnectionException(NodeCollectorRESTClient.UNREACHABLE); } } }