/** * 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.resources; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; import org.apache.wink.common.annotations.Parent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import com.abiquo.api.exceptions.APIError; import com.abiquo.api.exceptions.APIException; import com.abiquo.api.exceptions.ConflictException; import com.abiquo.api.exceptions.NotFoundException; import com.abiquo.api.services.DatacenterService; import com.abiquo.api.services.InfrastructureService; import com.abiquo.api.services.NetworkService; import com.abiquo.api.util.IRESTBuilder; import com.abiquo.model.enumerator.HypervisorType; import com.abiquo.model.enumerator.MachineState; import com.abiquo.model.enumerator.RemoteServiceType; import com.abiquo.model.rest.RESTLink; import com.abiquo.model.util.ModelTransformer; import com.abiquo.model.validation.Hypervisor; import com.abiquo.model.validation.Ip; import com.abiquo.model.validation.Port; import com.abiquo.server.core.cloud.HypervisorTypesDto; import com.abiquo.server.core.enterprise.Enterprise; import com.abiquo.server.core.enterprise.EnterprisesDto; import com.abiquo.server.core.infrastructure.Datacenter; import com.abiquo.server.core.infrastructure.DatacenterDto; import com.abiquo.server.core.infrastructure.Machine; import com.abiquo.server.core.infrastructure.MachineDto; import com.abiquo.server.core.infrastructure.MachineStateDto; import com.abiquo.server.core.infrastructure.MachinesDto; import com.abiquo.server.core.infrastructure.Rack; import com.abiquo.server.core.util.PagedList; import com.abiquo.server.core.util.network.IPAddress; @Parent(DatacentersResource.class) @Path(DatacenterResource.DATACENTER_PARAM) @Controller public class DatacenterResource extends AbstractResource { public static final String DATACENTER = "datacenter"; public static final String DATACENTER_PARAM = "{" + DATACENTER + "}"; public static final String HYPERVISORS_PATH = "hypervisors"; public static final String ENTERPRISES = "enterprises"; public static final String ENTERPRISES_PATH = "action/enterprises"; public static final String UPDATE_RESOURCES = "updateusedresources"; public static final String ENTERPRISES_REL = "enterprises"; public static final String UPDATE_RESOURCES_PATH = "action/updateusedresources"; public static final String NETWORK = "network"; public static final String ACTION_DISCOVER_SINGLE_PATH = "action/discoversingle"; public static final String ACTION_DISCOVER_SINGLE_REL = "discoversingle"; public static final String ACTION_DISCOVER_MULTIPLE_PATH = "action/discovermultiple"; public static final String ACTION_DISCOVER_MULTIPLE_REL = "discovermultiple"; public static final String ACTION_DISCOVER_HYPERVISOR_TYPE = "action/hypervisor"; public static final String ACTION_DISCOVER_HYPERVISOR_TYPE_REL = "hypervisor"; public static final String ACTION_MACHINES_CHECK = "action/checkmachinestate"; public static final String ACTION_MACHINES_CHECK_REL = "checkmachinestate"; public static final String ACTION_MACHINES_CHECK_IPMI = "action/checkmachineipmi"; public static final String ACTION_MACHINES_CHECK_IPMI_REL = "checkmachineipmi"; public static final String IP = "ip"; public static final String HYPERVISOR = "hypervisor"; public static final String USER = "user"; public static final String PASSWORD = "password"; public static final String PORT = "port"; public static final String IP_FROM = "ipFrom"; public static final String IP_TO = "ipTo"; public static final String VSWITCH = "vswitch"; public final static String URL = "url"; @Autowired DatacenterService service; @Autowired InfrastructureService infraService; @Autowired NetworkService netService; @Context UriInfo uriInfo; /** * Returns a datacenter * * @title Retrieve a datacenter * @param datacenterId identifier of the datacenter * @param restBuilder a Context-injected object to create the links of the Dto * @return a {DatacenterDto} object with the requested datacenter * @throws Exception */ @GET @Produces(DatacenterDto.MEDIA_TYPE) public DatacenterDto getDatacenter(@PathParam(DATACENTER) final Integer datacenterId, @Context final IRESTBuilder restBuilder) throws Exception { Datacenter datacenter = service.getDatacenter(datacenterId); return createTransferObject(datacenter, restBuilder); } /** * Modifies a datacenter * * @title Update an existing datacenter * @param datacenterDto datacenter to modify * @param datacenterId identifier of the datacenter * @param restBuilder a Context-injected object to create the links of the Dto * @return a {DatacenterDto} object with the modified datacenter * @throws Exception */ @PUT @Consumes(DatacenterDto.MEDIA_TYPE) @Produces(DatacenterDto.MEDIA_TYPE) public DatacenterDto modifyDatacenter(final DatacenterDto datacenterDto, @PathParam(DATACENTER) final Integer datacenterId, @Context final IRESTBuilder restBuilder) throws Exception { Datacenter datacenter = createPersistenceObject(datacenterDto); datacenter = service.modifyDatacenter(datacenterId, datacenter); return createTransferObject(datacenter, restBuilder); } /** * Returns all enterpises that are using the datacenter * * @title Retrive a list of Enterprises * @param datacenterId identifier of the datacenter * @param startwith * @param network * @param limit * @param restBuilder a Context-injected object to create the links of the Dto * @return an {EnterpriseDto} object with all enterprises that are using the datacenter * @throws Exception */ @GET @Path(ENTERPRISES_PATH) @Produces(EnterprisesDto.MEDIA_TYPE) public EnterprisesDto getEnterprises(@PathParam(DATACENTER) final Integer datacenterId, @QueryParam(START_WITH) @Min(0) final Integer startwith, @QueryParam(NETWORK) Boolean network, @QueryParam(LIMIT) @DefaultValue(DEFAULT_PAGE_LENGTH_STRING) @Min(1) final Integer limit, @Context final IRESTBuilder restBuilder) throws Exception { Integer firstElem = startwith == null ? 0 : startwith; Integer numElem = limit == null ? DEFAULT_PAGE_LENGTH : limit; if (network == null) { network = false; } Datacenter datacenter = service.getDatacenter(datacenterId); List<Enterprise> enterprises = service .findEnterprisesByDatacenterWithNetworks(datacenter, network, firstElem, numElem); EnterprisesDto enterprisesDto = new EnterprisesDto(); for (Enterprise e : enterprises) { enterprisesDto.add(EnterpriseResource.createTransferObject(e, restBuilder)); } enterprisesDto.setTotalSize(((PagedList) enterprises).getTotalResults()); enterprisesDto.addLinks(buildEnterprisesLinks(uriInfo.getAbsolutePath().toString(), (PagedList) enterprises, network, numElem)); return enterprisesDto; } /** * Returns available hypervisor types from a datacenter * * @title Retrive available hypervisor types * @param datacenterId identifier of the datacenter * @param restBuilder a Context-injected object to create the links of the Dto * @return an {HypervisorTypesDto} object with all available hypervisor types in the datacenter * @throws Exception */ @GET @Path(HYPERVISORS_PATH) @Produces(HypervisorTypesDto.MEDIA_TYPE) public HypervisorTypesDto getAvailableHypervisors( @PathParam(DATACENTER) final Integer datacenterId, @Context final IRESTBuilder restBuilder) throws Exception { Datacenter datacenter = service.getDatacenter(datacenterId); Set<HypervisorType> types = service.getHypervisorTypes(datacenter); HypervisorTypesDto dto = new HypervisorTypesDto(); dto.setCollection(new ArrayList<HypervisorType>(types)); return dto; } /** * Deletes a datacenter * * @title Delete an existing Datacenter * @param datacenterId identifier of the datacenter */ @DELETE public void deleteDatacenter(@PathParam(DATACENTER) final Integer datacenterId) { service.removeDatacenter(datacenterId); } /** * Updates used resources from a datacenter * * @title Update used resources from a datacenter * @param datacenterId identifier of the datacenter */ @PUT @Path(UPDATE_RESOURCES_PATH) public void updateUsedResources(@PathParam(DATACENTER) final Integer datacenterId) { Datacenter datacenter = service.getDatacenter(datacenterId); List<Rack> racks = service.getRacks(datacenter); for (Rack rack : racks) { List<Machine> machines = infraService.getMachines(rack); for (Machine machine : machines) { infraService.updateUsedResourcesByMachine(machine); } } } // --------- // // ACTIONS // // --------- // /** * Return the hypervisor type of a remote machine using the given @ip * * @title Retrieve the hypervisor type from remote machine * @param datacenterId The ID of the datacenter where this remote service is assigned. * @param ip The IP of the target cloud node. * @return The Hypervisor Type. * @throws Exception If the hypervisor type information cannot be retrieved. */ @GET @Path(ACTION_DISCOVER_HYPERVISOR_TYPE) @Produces(MediaType.TEXT_PLAIN) public String getHypervisorType( @PathParam(DatacenterResource.DATACENTER) final Integer datacenterId, @QueryParam(IP) @NotNull final String ip) { validatePathParameters(datacenterId); return infraService.discoverRemoteHypervisorType(datacenterId, ip).getValue(); } /** * Returns back the parameters to create a physical machine. * * @title Retrieve remote machine information * @wiki This feature is used to retrieve information from a remote machine giving its user, * password and hypervisor type. The response entity is a Machine that can be used to * copy-paste the entity in physical machine creation. Behind the scenes, it uses the * Discovery Manager remote service. Since the Discovery Manager data model is not fully * compatible with the API data model, you will find the <vswitch /> tag has a trailing * slash with all the virtual switches found. Moreover, all the datastores <enabled> tags * are always set to "false" and physical machine creation requires at least one datastore * enabled. Please edit these values in the Machine before calling physical machine * creation. * @param datacenterId identifier of the datacenter. It is useful to search the uri of the * DiscoveryManager to call. * @param ip ip address of the hypevisor * @param hypervisorType {@link HypervisorType} * @param user user to log in. * @param password password to log in. * @param port port address to query the request of the hypervisor. Only useful in KVM and XEN * hypervisor types. * @param restBuilder injected context REST link builder. * @return a MachineDto * @throws Exception */ @GET @Path(ACTION_DISCOVER_SINGLE_PATH) @Produces(MachineDto.MEDIA_TYPE) public MachineDto discoverSingleMachine( @PathParam(DATACENTER) @NotNull @Min(1) final Integer datacenterId, @QueryParam(IP) @Ip final String ip, @QueryParam(HYPERVISOR) @Hypervisor final String hypervisorType, @QueryParam(USER) @NotNull final String user, @QueryParam(PASSWORD) @NotNull final String password, @QueryParam(PORT) @Port @DefaultValue("8889") final Integer port, @Context final IRESTBuilder restBuilder) throws Exception { Machine machine = infraService.discoverRemoteHypervisor(datacenterId, IPAddress.newIPAddress(ip), HypervisorType.fromValue(hypervisorType), user, password, port); return MachineResource.createTransferObject(machine, restBuilder); } /** * Returns back the list of physical machines that match the request of @hypervisorType, @user * and @password inside the values @ipFrom and @ipTo. * * @title Retrieve a list of remote machine information * @wiki This feature is used to retrieve information from a list of remote machines giving the * user, password and hypervisor type of each machine. You can also filter the search by * vswitch value. The response entity is a list of Machine that can be used to create * multiple physical machines. Behind the scenes, it uses the Discovery Manager remote * service. The Discovery Manager data model is not fully compatible with the API data * model, so you may find the <vswitch /> tag with a trailing slash with all the virtual * switches found in a machine. Moreover, all the datastores <enabled> tags are always set * to "false" and physical machine creation requires at least one datastore enabled. * Please edit these values in the Machine before calling the create multiple physical * machines method. * @param datacenterId identifier of the datacenter to search the corresponding Discovery * Manager. * @param ipFrom first ip to look for * @param ipTo last ip to look for * @param hypervisorType kind of hypervisor. * @param user user to log in * @param password password to log in. * @param port port address to query the request of the hypervisor. Only useful in KVM and XEN * hypervisor types. * @param restBuilder injected context REST link builder. * @return a {@link MachinesDto} wrapper object with all the physical machines. * @throws Exception */ @GET @Path(ACTION_DISCOVER_MULTIPLE_PATH) @Produces(MachinesDto.MEDIA_TYPE) public MachinesDto discoverMultipleMachine( @PathParam(DATACENTER) @NotNull @Min(1) final Integer datacenterId, @QueryParam(IP_FROM) @Ip final String ipFrom, @QueryParam(IP_TO) @Ip final String ipTo, @QueryParam(HYPERVISOR) @Hypervisor final String hypervisorType, @QueryParam(USER) @NotNull final String user, @QueryParam(PASSWORD) @NotNull final String password, @QueryParam(PORT) @Port @DefaultValue("8889") final Integer port, @QueryParam(VSWITCH) final String vswitch, @Context final IRESTBuilder restBuilder) throws Exception { List<Machine> machines = infraService.discoverRemoteHypevisors(datacenterId, IPAddress.newIPAddress(ipFrom), IPAddress.newIPAddress(ipTo), HypervisorType.fromValue(hypervisorType), user, password, port, vswitch); return MachinesResource.transformMachinesDto(restBuilder, machines); } /** * Checks the machine state. * * @title Check the state from remote machine * @wiki This feature is used to check the state from a remote machine Machine giving its user, * password and hypervisor type. This machine does not need to be managed by abiquo. The * response entity is a MachineState. Behind the scenes, it uses the Discovery Manager * remote service. * @param datacenterId The ID of the datacenter where this remote service are assigned. * @param ip The IP of the target cloud node. * @param hypervisorType The cloud node hypervisor type. * @param user The hypervisor user. * @param password The hypervisor password. * @param port The hypervisor AIM port. * @return The actual machine's state. */ @GET @Path(ACTION_MACHINES_CHECK) @Produces(MachineStateDto.MEDIA_TYPE) public MachineStateDto checkMachineState( @PathParam(DatacenterResource.DATACENTER) final Integer datacenterId, @PathParam(RackResource.RACK) final Integer rackId, @QueryParam("ip") @NotNull final String ip, @QueryParam("hypervisor") @NotNull final HypervisorType hypervisorType, @QueryParam("user") @NotNull final String user, @QueryParam("password") @NotNull final String password, @QueryParam("port") @NotNull final Integer port, @Context final IRESTBuilder restBuilder) throws Exception { try { MachineState state = infraService.checkMachineState(datacenterId, ip, hypervisorType, user, password, port); MachineStateDto dto = new MachineStateDto(); dto.setState(state); return dto; } catch (Exception e) { throw translateException(e); } } /** * Checks the ipmi configuration * * @title Check IPMI configuration from remote machine * @wiki This feature is used to check the configuration from a remote machine Machine giving * its user, password and port. This machine does not need to be managed by abiquo. Behind * the scenes, it uses the Discovery Manager remote service. * @param datacenterId The ID of the datacenter where this remote service is assigned. * @param ip The IP of the target cloud node. * @param user The hypervisor user. * @param password The hypervisor password. * @param port The hypervisor AIM port. */ @GET @Path(ACTION_MACHINES_CHECK_IPMI) public void isStonithUp(@PathParam(DatacenterResource.DATACENTER) final Integer datacenterId, @PathParam(RackResource.RACK) final Integer rackId, @PathParam(MachineResource.MACHINE) final Integer machineId, @QueryParam("ip") @NotNull final String ip, @QueryParam("user") @NotNull final String user, @QueryParam("password") @NotNull final String password, @QueryParam("port") final Integer port, @Context final IRESTBuilder restBuilder) throws Exception { infraService.isStonithUp(datacenterId, ip, user, password, port); } // no resources response public static DatacenterDto addLinks(final IRESTBuilder builder, final DatacenterDto datacenter) { datacenter.setLinks(builder.buildDatacenterLinks(datacenter)); return datacenter; } public static DatacenterDto createTransferObject(final Datacenter datacenter, final IRESTBuilder builder) throws Exception { DatacenterDto dto = ModelTransformer.transportFromPersistence(DatacenterDto.class, datacenter); dto = addLinks(builder, dto); return dto; } // Create the persistence object. public static Datacenter createPersistenceObject(final DatacenterDto datacenter) throws Exception { return ModelTransformer.persistenceFromTransport(Datacenter.class, datacenter); } // not exposed methods /** * Translates the Node Collector client exception into a {@link WebApplicationException}. * * @param e The Exception to transform. * @return The transformed Exception. */ private APIException translateException(final Exception e) { return new ConflictException(APIError.NODECOLLECTOR_ERROR); } /** * Checks if the remote service is assigned to the specified datacenter. * * @param datacenterId The provided ID of the datacenter. * @param serviceType The unique type of this remote service. * @throws NotFoundException If the remote service is not assigned to the provided datacenter. */ private void validatePathParameters(final Integer datacenterId) throws NotFoundException { if (!service.isAssignedTo(datacenterId, RemoteServiceType.NODE_COLLECTOR)) { throw new NotFoundException(APIError.NOT_ASSIGNED_REMOTE_SERVICE_DATACENTER); } } private List<RESTLink> buildEnterprisesLinks(final String Path, final PagedList< ? > list, final Boolean network, final Integer numElem) { List<RESTLink> links = new ArrayList<RESTLink>(); links.add(new RESTLink("first", Path)); if (list.getCurrentElement() != 0) { Integer previous = list.getCurrentElement() - list.getPageSize(); previous = previous < 0 ? 0 : previous; links.add(new RESTLink("prev", Path + "?" + NETWORK + "=" + network.toString() + '&' + AbstractResource.START_WITH + "=" + previous + '&' + AbstractResource.LIMIT + "=" + numElem)); } Integer next = list.getCurrentElement() + list.getPageSize(); if (next < list.getTotalResults()) { links.add(new RESTLink("next", Path + "?" + NETWORK + "=" + network.toString() + '&' + AbstractResource.START_WITH + "=" + next + '&' + AbstractResource.LIMIT + "=" + numElem)); } Integer last = list.getTotalResults() - list.getPageSize(); if (last < 0) { last = 0; } links.add(new RESTLink("last", Path + "?" + NETWORK + "=" + network.toString() + '&' + AbstractResource.START_WITH + "=" + last + '&' + AbstractResource.LIMIT + "=" + numElem)); return links; } }