/** * 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.api.services.stub; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import com.abiquo.api.exceptions.APIError; import com.abiquo.api.exceptions.ConflictException; import com.abiquo.api.services.DefaultApiService; import com.abiquo.model.enumerator.DiskFormatType; import com.abiquo.model.enumerator.HypervisorType; import com.abiquo.model.enumerator.MachineState; import com.abiquo.model.transport.error.CommonError; import com.abiquo.nodecollector.client.NodeCollectorRESTClient; 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.appslibrary.VirtualMachineTemplate; import com.abiquo.server.core.cloud.Hypervisor; import com.abiquo.server.core.cloud.VirtualMachine; import com.abiquo.server.core.cloud.VirtualMachineState; import com.abiquo.server.core.infrastructure.Datacenter; import com.abiquo.server.core.infrastructure.Datastore; import com.abiquo.server.core.infrastructure.InfrastructureRep; import com.abiquo.server.core.infrastructure.Machine; import com.abiquo.server.core.infrastructure.RemoteService; import com.abiquo.server.core.infrastructure.nodecollector.HostDto; import com.abiquo.server.core.infrastructure.nodecollector.HostStatusEnumType; import com.abiquo.server.core.infrastructure.nodecollector.ResourceEnumType; import com.abiquo.server.core.infrastructure.nodecollector.ResourceType; import com.abiquo.server.core.infrastructure.nodecollector.VirtualDiskEnumType; import com.abiquo.server.core.infrastructure.nodecollector.VirtualSystemCollectionDto; import com.abiquo.server.core.infrastructure.nodecollector.VirtualSystemDto; import com.abiquo.server.core.infrastructure.storage.DiskManagement; import com.abiquo.server.core.util.network.IPAddress; /** * Wraps the calls nodecollector using {@link NodeCollectorPremiumRESTClient} providing an easy way * to mock it for test pruposes. Also transform NodeCollector's exceptions to any Abiquo API common * exception. * <p> * We pass the {@link RemoteService} object in every method. That means the services that call this * service need to get the {@link RemoteService} before to call it. That decouples this logic from * the persistent framework and also brings a more easy way to test it. * * @author jdevesa@abiquo.com */ @Service public class NodecollectorServiceStub extends DefaultApiService { private final Logger logger = LoggerFactory.getLogger(NodecollectorServiceStub.class); @Autowired InfrastructureRep infrastructureRep; /** * Return kind of hypervisor API the remote machine through the * {@link NodeCollectorPremiumRESTClient} client. * * @param nodecollector object nodecollector used in the datacenter. * @param hypervisorIp ip address of the hypervisor. * @return hypType kind of hypervisor API of remote machine. */ public HypervisorType getRemoteHypervisorType(final RemoteService nodecollector, final String hypervisorIp) { NodeCollectorRESTClient restCli = initializeRESTClient(nodecollector); checkIPformat(hypervisorIp); try { return restCli.getRemoteHypervisorType(hypervisorIp); } catch (BadRequestException e) { // InternalServerError -> A Bad Request NEVER should be thrown from here. logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (ConnectionException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_CONNECTION_EXCEPTION); } catch (UnprovisionedException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_NOT_FOUND_EXCEPTION); } catch (CollectorException e) { logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (ServiceUnavailableException e) { logger.error(e.getMessage()); addServiceUnavailableErrors(APIError.NC_UNAVAILABLE_EXCEPTION); } catch (CannotExecuteException e) { addConflictErrors(new CommonError(APIError.STATUS_CONFLICT.getCode(), e.getMessage())); } catch (NoManagedException e) { addConflictErrors(new CommonError(APIError.NC_NOT_MANAGED_HOST.getCode(), e .getMessage())); } flushErrors(); return null; } private void checkIPformat(final String hypervisorIp) { try { InetAddress addr = InetAddress.getByName(hypervisorIp); } catch (UnknownHostException e) { addConflictErrors(APIError.NC_INVALID_IP); flushErrors(); } } /** * Return the list of machines in the range of ips from the parameter {ipFrom} to parameter * {ipTo} that matches with the hypervisortype, user, and password. For any error in the * retrieve, the machine will be ignored. Uses the method * {@link #getRemoteHypervisor(RemoteService, IPAddress, HypervisorType, String, String, Integer)} * for all the links. * * @param nodecollector {@link RemoteService} object * @param ipFrom first address to search. * @param ipTo last address to search. * @param hypType {@link HypervisorType} object. * @param user user to login * @param password password to login * @param port aim port in case we want to retrieve KVM or XEN. * @return list of Machines. */ public List<Machine> getRemoteHypervisors(final RemoteService nodecollector, final IPAddress ipFrom, final IPAddress ipTo, final HypervisorType hypType, final String user, final String password, final Integer port) { if (ipFrom.isBiggerThan(ipTo)) { // TODO: Test here addConflictErrors(APIError.NETWORK_IP_FROM_BIGGER_THAN_IP_TO); flushErrors(); } List<Machine> listOfMachines = new ArrayList<Machine>(); IPAddress currentIp = ipFrom; while (!currentIp.isBiggerThan(ipTo)) { try { Machine machine = this.getRemoteHypervisor(nodecollector, currentIp, hypType, user, password, port); listOfMachines.add(machine); } catch (ConflictException e) { // any conflict exception we found, we ignore it. We return only // the machines that are 'exactly' with the user, password and hypType we have // informed. } currentIp = currentIp.nextIPAddress(); } return listOfMachines; } /** * Return the remote machine using its hypervisor APIs through the * {@link NodeCollectorPremiumRESTClient} client. * * @param nodecollector object nodecollector used in the datacenter we want to add the machine. * @param hypervisorIp ip address of the hypervisor. * @param hypType kind of hypervisor API we want to use. * @param user use to log-in * @param password password * @param port (optional) AIM port we connect to if we use KVM or XEN. * @return the remote {@link Machine} entity, null if we don't find anything. */ public Machine getRemoteHypervisor(final RemoteService nodecollector, final IPAddress hypervisorIp, final HypervisorType hypType, final String user, final String password, final Integer port) { NodeCollectorRESTClient restCli = initializeRESTClient(nodecollector); HostDto host = new HostDto(); try { host = restCli.getRemoteHostInfo(hypervisorIp.toString(), hypType, user, password, port); } catch (BadRequestException e) { // InternalServerError -> A Bad Request NEVER should be thrown from here. logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (LoginException e) { logger.debug(e.getMessage()); if (hypType == HypervisorType.HYPERV_301) { // Hyper-V is the only hypervisor that returns the same message for bad credentials // that bad configuration. We must add this extra info to the user. logger.debug("hyperv"); addConflictErrors(APIError.NC_BAD_CONFIGURATION); flushErrors(); } addConflictErrors(APIError.NC_BAD_CREDENTIALS_TO_MACHINE); } catch (ConnectionException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_CONNECTION_EXCEPTION); } catch (UnprovisionedException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_NOT_FOUND_EXCEPTION); } catch (CollectorException e) { logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (ServiceUnavailableException e) { logger.error(e.getMessage()); addServiceUnavailableErrors(APIError.NC_UNAVAILABLE_EXCEPTION); } catch (CannotExecuteException e) { addConflictErrors(new CommonError(APIError.STATUS_CONFLICT.getCode(), e.getMessage())); } catch (NoManagedException e) { addConflictErrors(new CommonError(APIError.NC_NOT_MANAGED_HOST.getCode(), e .getMessage())); } flushErrors(); Machine machine = hostToMachine(nodecollector.getDatacenter(), host); Hypervisor hypervisor = machine.createHypervisor(hypType, hypervisorIp.toString(), hypervisorIp.toString(), port, user, password); // machine.setHypervisor(hypervisor); return machine; } /** * Return kind of hypervisor API the remote machine through the * {@link NodeCollectorPremiumRESTClient} client. * * @param nodecollector object nodecollector used in the datacenter. * @param hypervisorIp ip address of the hypervisor. * @return hypType kind of hypervisor API of remote machine. * @throws LoginException */ public List<VirtualMachine> getRemoteVirtualMachines(final RemoteService nodecollector, final String hypervisorIp, final HypervisorType hypervisorType, final String user, final String password, final Integer aimport) { NodeCollectorRESTClient restCli = initializeRESTClient(nodecollector); try { VirtualSystemCollectionDto vsc = restCli.getRemoteVirtualSystemCollection(hypervisorIp, hypervisorType, user, password, aimport); List<VirtualMachine> vms = transportVSCollectionToVMs(vsc); return vms; } catch (BadRequestException e) { // InternalServerError -> A Bad Request NEVER should be thrown from here. logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (LoginException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_BAD_CREDENTIALS_TO_MACHINE); } catch (ConnectionException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_CONNECTION_EXCEPTION); } catch (UnprovisionedException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_NOT_FOUND_EXCEPTION); } catch (CollectorException e) { logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (CannotExecuteException e) { addConflictErrors(new CommonError(APIError.STATUS_CONFLICT.getCode(), e.getMessage())); } catch (NoManagedException e) { addConflictErrors(new CommonError(APIError.NC_NOT_MANAGED_HOST.getCode(), e .getMessage())); } flushErrors(); return null; } /** * Return kind of hypervisor API the remote machine through the * {@link NodeCollectorPremiumRESTClient} client. * * @param nodecollector object nodecollector used in the datacenter. * @param hypervisorIp ip address of the hypervisor. * @return hypType kind of hypervisor API of remote machine. * @throws LoginException */ public VirtualMachine getRemoteVirtualMachineByUUID(final RemoteService nodecollector, final String hypervisorIp, final HypervisorType hypervisorType, final String user, final String password, final Integer aimport, final String uuid) { NodeCollectorRESTClient restCli = initializeRESTClient(nodecollector); try { VirtualSystemDto vs = restCli.getRemoteVirtualSystemByUUID(uuid, hypervisorIp, hypervisorType, user, password, aimport); VirtualMachine vm = transportVSToVM(vs); return vm; } catch (BadRequestException e) { // InternalServerError -> A Bad Request NEVER should be thrown from here. logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (LoginException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_BAD_CREDENTIALS_TO_MACHINE); } catch (ConnectionException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_CONNECTION_EXCEPTION); } catch (UnprovisionedException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_VIRTUAL_MACHINE_NOT_FOUND); } catch (CollectorException e) { logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (CannotExecuteException e) { addConflictErrors(new CommonError(APIError.STATUS_CONFLICT.getCode(), e.getMessage())); } catch (NoManagedException e) { addConflictErrors(new CommonError(APIError.NC_NOT_MANAGED_HOST.getCode(), e .getMessage())); } flushErrors(); return null; } /** * Queries for a single virtual machine to the nodecollector based on its name. * * @param nodecollector nodecollector remote service to call. * @param hypervisorIp ip to the remote physical machine * @param hypervisorType kind of hypervisor running * @param user user to log in into the remote hypervisor. * @param password password to authenticate to the remote hypervisor. * @param aimport port to connect (libvirt based hypervisors only) * @param name name of the virtual machine we look for. * @return a {@link VirtualMachine} object with the requested machine. */ public VirtualMachine getRemoteVirtualMachineByName(final RemoteService nodecollector, final String hypervisorIp, final HypervisorType hypervisorType, final String user, final String password, final Integer aimport, final String name) { NodeCollectorRESTClient restCli = initializeRESTClient(nodecollector); try { VirtualSystemDto vs = restCli.getRemoteVirtualSystemByName(name, hypervisorIp, hypervisorType, user, password, aimport); VirtualMachine vm = transportVSToVM(vs); return vm; } catch (BadRequestException e) { // InternalServerError -> A Bad Request NEVER should be thrown from here. logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (LoginException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_BAD_CREDENTIALS_TO_MACHINE); } catch (ConnectionException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_CONNECTION_EXCEPTION); } catch (UnprovisionedException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_VIRTUAL_MACHINE_NOT_FOUND); } catch (CollectorException e) { logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (CannotExecuteException e) { addConflictErrors(new CommonError(APIError.STATUS_CONFLICT.getCode(), e.getMessage())); } catch (NoManagedException e) { addConflictErrors(new CommonError(APIError.NC_NOT_MANAGED_HOST.getCode(), e .getMessage())); } flushErrors(); return null; } public Boolean isStonithUp(final RemoteService nodecollector, final String ip, final Integer port, final String username, final String password) { NodeCollectorRESTClient rest = initializeRESTClient(nodecollector); try { return rest.isStonithUp(ip, port, username, password); } catch (BadRequestException e) { // InternalServerError -> A Bad Request NEVER should be thrown from here. logger.error(e.getMessage()); addUnexpectedErrors(APIError.NC_UNEXPECTED_EXCEPTION); } catch (ConnectionException e) { logger.debug(e.getMessage()); addConflictErrors(APIError.NC_CONNECTION_EXCEPTION); } flushErrors(); return null; } // private methods /** * Converts the type of {@link HostDto} from the nodecollector response to the {@link Machine} * type for the API model. * * @param datacenter datacenter where the machine will be assigned. * @param host hostDto object. * @return a {@link Machine} entity. */ protected Machine hostToMachine(final Datacenter datacenter, final HostDto host) { final Integer MEGABYTE = 1048576; int ram = (int) (host.getRam() / MEGABYTE); int cpus = (int) host.getCpu(); Machine machine = new Machine(datacenter, host.getName(), host.getDescription(), ram, 0, cpus, 0, transfromToState(host.getStatus()), ""); // Long totalStorage = 0L; String switches = ""; for (ResourceType resource : host.getResources()) { // TODO remove code if (resource.getResourceType().equals(ResourceEnumType.HARD_DISK)) { Datastore datastore = new Datastore(machine, resource.getElementName(), resource.getAddress(), ""); datastore.setEnabled(Boolean.FALSE); datastore.setSize(resource.getUnits()); datastore.setUsedSize(resource.getUnits() - resource.getAvailableUnits()); if (resource.getConnection() == null) { datastore.setDatastoreUUID(UUID.randomUUID().toString()); } else { datastore.setDatastoreUUID(resource.getConnection()); } } else { if (resource.getResourceType().equals(ResourceEnumType.NETWORK_INTERFACE)) { switches = switches.concat(resource.getElementName()) + "/"; machine.getListOfMacs().add(resource.getAddress()); } } } switches = StringUtils.hasLength(switches) ? switches.substring(0, switches.lastIndexOf('/')) : null; machine.setVirtualSwitch(switches); return machine; } /** * Transform the NodeCollector's enum state {@link HostStatusEnumType} to Server enum state * {@link MachineState} * * @param status status of the nodecollector's retrieval. * @return */ private MachineState transfromToState(final HostStatusEnumType status) { switch (status) { case MANAGED: return MachineState.MANAGED; case NOT_MANAGED: return MachineState.NOT_MANAGED; case PROVISIONED: return MachineState.PROVISIONED; default: return MachineState.STOPPED; } } protected NodeCollectorRESTClient initializeRESTClient(final RemoteService nodecollector) { return new NodeCollectorRESTClient(nodecollector.getUri()); } /** * Returns a list of virtual machines builded from a virtual system list * * @param vsc VirtualSystemCollectionDto list to transform * @return list of virtual machines builded from a virtual system list */ private List<VirtualMachine> transportVSCollectionToVMs(final VirtualSystemCollectionDto vsc) { List<VirtualMachine> vms = new ArrayList<VirtualMachine>(); for (VirtualSystemDto vs : vsc.getVirtualSystems()) { VirtualMachine vm = transportVSToVM(vs); // ABICLOUDPREMIUM-3405: abiquo cannot manage virtual machines without primary disk. So // we skip them. if (vm.getVirtualMachineTemplate() != null) { vms.add(vm); } } return vms; } /** * Returns a single virtual machine from a virtual system. * * @param vs NodeCollector's Virtual System. * @return the transformed {@link VirtualMachine} object. */ protected VirtualMachine transportVSToVM(final VirtualSystemDto vs) { long MEGABYTE = 1024L * 1024L; VirtualMachine vm = new VirtualMachine(vs.getName(), null, null, null, null, UUID.fromString(vs.getUuid()), VirtualMachine.NOT_MANAGED); vm.setCpu(new Long(vs.getCpu()).intValue()); vm.setRam(new Long(vs.getRam() / MEGABYTE).intValue()); vm.setVdrpPort(new Long(vs.getVport()).intValue()); vm.setState(VirtualMachineState.valueOf(vs.getStatus().value())); vm.setDisks(new ArrayList<DiskManagement>()); for (ResourceType rt : vs.getResources()) { if (rt.getLabel() == null || rt.getLabel().equals("SYSTEM DISK")) { long bytesHD = rt.getUnits(); vm.setHdInBytes(bytesHD); if (StringUtils.hasText(rt.getConnection())) { Datastore ds = new Datastore(); ds.setDirectory(rt.getAddress()); ds.setRootPath(rt.getConnection()); ds.setName(rt.getElementName()); vm.setDatastore(ds); } VirtualMachineTemplate vi = new VirtualMachineTemplate(); // XXX this is not stored // in the DDBB VirtualDiskEnumType diskFormatType = VirtualDiskEnumType.fromValue(rt.getResourceSubType().toString()); vi.setDiskFormatType(DiskFormatType.fromURI(diskFormatType.value())); vi.setPath(rt.getAddress()); if (diskFormatType.equals(VirtualDiskEnumType.STATEFUL)) { vi.setStateful(Boolean.TRUE); } vi.setDiskFileSize(rt.getUnits()); vm.setVirtualMachineTemplate(vi); vm.setHdInBytes(rt.getUnits()); if (rt.getLabel() == null) { break; } } else { DiskManagement disky = new DiskManagement(null, rt.getUnits() / MEGABYTE); disky.setSizeInMb(rt.getUnits() / MEGABYTE); vm.getDisks().add(disky); } } return vm; } }