/**
* 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 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.WebApplicationException;
import javax.ws.rs.core.Context;
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.InfrastructureService;
import com.abiquo.api.services.MachineService;
import com.abiquo.api.services.cloud.VirtualApplianceService;
import com.abiquo.api.services.cloud.VirtualDatacenterService;
import com.abiquo.api.services.cloud.VirtualMachineService;
import com.abiquo.api.util.IRESTBuilder;
import com.abiquo.model.enumerator.HypervisorType;
import com.abiquo.model.enumerator.MachineState;
import com.abiquo.model.util.ModelTransformer;
import com.abiquo.server.core.cloud.Hypervisor;
import com.abiquo.server.core.enterprise.Enterprise;
import com.abiquo.server.core.infrastructure.Datastore;
import com.abiquo.server.core.infrastructure.DatastoreDto;
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;
@Parent(MachinesResource.class)
@Path(MachineResource.MACHINE_PARAM)
@Controller
public class MachineResource extends AbstractResource
{
public static final String MACHINE = "machine";
public static final String MACHINE_PARAM = "{" + MACHINE + "}";
public static final String MOVE_TARGET_QUERY_PARAM = "target";
public static final String MACHINE_ACTION_GET_VIRTUALMACHINES_PATH = "action/virtualmachines";
public static final String MACHINE_ACTION_POWER_OFF_PATH = "action/poweroff";
public static final String MACHINE_ACTION_POWER_OFF_REL = "poweroff";
public static final String MACHINE_ACTION_POWER_ON_PATH = "action/poweron";
public static final String MACHINE_ACTION_POWER_ON_REL = "poweron";
public static final String MACHINE_ACTION_CHECK = "action/checkstate";
public static final String MACHINE_CHECK = "checkstate";
public static final String MACHINE_ACTION_LED_ON = "action/ledon";
public static final String MACHINE_ACTION_LED_ON_REL = "ledon";
public static final String MACHINE_ACTION_LS = "logicserver";
public static final String SHOW_CREDENTIALS_QUERY_PARAM = "credentials";
public static final String MACHINE_ACTION_GET_VIRTUALMACHINES = "action/virtualmachines";
public static final String MACHINE_ACTION_LED_OFF = "action/ledoff";
public static final String MACHINE_ACTION_LED_OFF_REL = "ledoff";
public static final String MACHINE_ACTION_LS__REL = "logicserver";
public static final String MACHINE_LOCATOR_LED = "led";
public static final String MACHINE_LOCATOR_LED_REL = "led";
@Autowired
MachineService service;
@Autowired
InfrastructureService infraService;
@Autowired
VirtualMachineService vmService;
@Autowired
VirtualDatacenterService vdcService;
@Autowired
VirtualApplianceService vappService;
/**
* Returns a machine
*
* @title Retrieve a Machine
* @param datacenterId identifier of the datacenter
* @param rackId identifier of the rack
* @param machineId identifier of the machine
* @param showCredentials boolean to indicate if the machine credentials will be returned
* @param restBuilder a Context-injected object to create the links of the Dto
* @return a {machineDto} object with the requested machines
* @throws Exception
*/
@GET
@Produces(MachineDto.MEDIA_TYPE)
public MachineDto getMachine(
@PathParam(DatacenterResource.DATACENTER) final Integer datacenterId,
@PathParam(RackResource.RACK) final Integer rackId,
@PathParam(MACHINE) final Integer machineId,
@QueryParam(SHOW_CREDENTIALS_QUERY_PARAM) final Boolean showCredentials,
@Context final IRESTBuilder restBuilder) throws Exception
{
validatePathParameters(datacenterId, rackId, machineId);
Machine machine = service.getMachine(machineId);
MachineDto dto = createTransferObject(machine, restBuilder);
// Credentials are only returned if they are requested
if (showCredentials != null && showCredentials.equals(Boolean.TRUE)
&& machine.getHypervisor() != null)
{
dto.setUser(machine.getHypervisor().getUser());
dto.setPassword(machine.getHypervisor().getPassword());
}
return dto;
}
/**
* Modifies a machine
*
* @title Update an existing Machine
* @param datacenterId idenfier of the datacenter
* @param rackId identifier of the rack
* @param machineId identifier of the machine
* @param machine machine to modify
* @param restBuilder a Context-injected object to create the links of the Dto
* @return a {MachineDto} objec with the modified machine
* @throws Exception
*/
@PUT
@Consumes(MachineDto.MEDIA_TYPE)
@Produces(MachineDto.MEDIA_TYPE)
public MachineDto modifyMachine(
@PathParam(DatacenterResource.DATACENTER) final Integer datacenterId,
@PathParam(RackResource.RACK) final Integer rackId,
@PathParam(MACHINE) final Integer machineId, final MachineDto machine,
@Context final IRESTBuilder restBuilder) throws Exception
{
validatePathParameters(datacenterId, rackId, machineId);
Machine old = service.getMachine(machineId);
// if we enable the machine then we force a check
if (old.getState().equals(MachineState.HALTED)
&& !machine.getState().equals(MachineState.HALTED))
{
MachineState newState =
infraService.checkMachineState(datacenterId, machine.getIp(), machine.getType(),
old.getHypervisor().getUser(), old.getHypervisor().getPassword(),
machine.getPort());
// machine will be updated with the given state
machine.setState(newState);
}
Machine m = service.modifyMachine(machineId, machine);
return createTransferObject(m, restBuilder);
}
/**
* Deletes a machine.
*
* @title Delete a Machine
* @param datacenterId identifier of the datacenter
* @param rackId identifier of the rack
* @param machineId identifier of the machine
*/
@DELETE
public void deleteMachine(@PathParam(DatacenterResource.DATACENTER) final Integer datacenterId,
@PathParam(RackResource.RACK) final Integer rackId,
@PathParam(MACHINE) final Integer machineId)
{
validatePathParameters(datacenterId, rackId, machineId);
service.removeMachine(machineId);
}
/**
* Checks the machine state and updates it.
*
* @title Check Machine state
* @wiki This feature checks physical machine state using the discovery manager and returns it.
* The sync query parameter allows you to update the database with the value of state
* returned by the discovery manager.
* @param datacenterId The ID of the datacenter where this remote service and machine 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.
* @param restBuilder a Context-injected object to create the links of the Dto
* @return a {MachineStateDto} object with the actual machine's state.
*/
@GET
@Path(MACHINE_ACTION_CHECK)
@Produces(MachineStateDto.MEDIA_TYPE)
public MachineStateDto checkMachineState(
@PathParam(DatacenterResource.DATACENTER) final Integer datacenterId,
@PathParam(RackResource.RACK) final Integer rackId,
@PathParam(MachineResource.MACHINE) final Integer machineId,
@QueryParam("sync") @DefaultValue("false") final boolean sync,
@Context final IRESTBuilder restBuilder) throws Exception
{
try
{
Machine m = service.getMachine(machineId);
Hypervisor h = m.getHypervisor();
MachineState state =
infraService.checkMachineState(datacenterId, h.getIp(), h.getType(), h.getUser(),
h.getPassword(), h.getPort());
if (sync)
{
m.setState(state);
MachineDto machineDto = createTransferObject(m, restBuilder);
service.modifyMachine(machineId, machineDto);
}
MachineStateDto dto = new MachineStateDto();
dto.setState(state);
return dto;
}
catch (Exception e)
{
throw translateException(e);
}
}
// protected methods
protected Hypervisor getHypervisor(final Integer datacenterId, final Integer rackId,
final Integer machineId)
{
if (!service.isAssignedTo(datacenterId, rackId, machineId))
{
throw new NotFoundException(APIError.NOT_ASSIGNED_MACHINE_DATACENTER_RACK);
}
Hypervisor hypervisor = service.getMachine(machineId).getHypervisor();
if (hypervisor == null)
{
throw new NotFoundException(APIError.VIRTUAL_MACHINE_WITHOUT_HYPERVISOR);
}
return hypervisor;
}
protected static MachineDto addLinks(final IRESTBuilder restBuilder,
final Integer datacenterId, final Integer rackId, final Boolean managedRack,
final Enterprise enterprise, final MachineDto machine)
{
machine.setLinks(restBuilder.buildMachineLinks(datacenterId, rackId, managedRack,
enterprise, machine));
return machine;
}
public static MachineDto createTransferObject(final Machine machine,
final IRESTBuilder restBuilder) throws Exception
{
MachineDto dto = new MachineDto();
dto.setDescription(machine.getDescription());
dto.setId(machine.getId());
dto.setName(machine.getName());
dto.setState(machine.getState());
dto.setVirtualCpuCores(machine.getVirtualCpuCores());
dto.setVirtualCpusUsed(machine.getVirtualCpusUsed());
dto.setVirtualRamInMb(machine.getVirtualRamInMb());
dto.setVirtualRamUsedInMb(machine.getVirtualRamUsedInMb());
dto.setVirtualSwitch(machine.getVirtualSwitch());
dto.setIpmiIP(machine.getIpmiIP());
dto.setIpmiPort(machine.getIpmiPort());
dto.setIpmiUser(machine.getIpmiUser());
dto.setIpmiPassword(machine.getIpmiPassword());
dto.setInitiatorIQN(machine.getInitiatorIQN());
if (machine.getHypervisor() != null)
{
dto.setIp(machine.getHypervisor().getIp());
dto.setIpService(machine.getHypervisor().getIpService());
dto.setType(machine.getHypervisor().getType());
dto.setPort(machine.getHypervisor().getPort());
// Credentials are not returned by default
// dto.setUser(machine.getHypervisor().getUser());
// dto.setPassword(machine.getHypervisor().getPassword());
}
if (machine.getDatastores() != null)
{
for (Datastore datastore : machine.getDatastores())
{
DatastoreDto dataDto = new DatastoreDto();
dataDto.setDirectory(datastore.getDirectory());
dataDto.setEnabled(datastore.isEnabled());
dataDto.setId(datastore.getId());
dataDto.setName(datastore.getName());
dataDto.setRootPath(datastore.getRootPath());
dataDto.setSize(datastore.getSize());
dataDto.setUsedSize(datastore.getUsedSize());
dataDto.setDatastoreUUID(datastore.getDatastoreUUID());
dto.getDatastores().add(dataDto);
}
}
// if the machine comes from the discovery manager it is not already saved in database and
// it does not have
// any rack nor datacenter. Don't build the links.
if (machine.getRack() != null)
{
dto =
addLinks(restBuilder, machine.getDatacenter().getId(), machine.getRack().getId(),
machine.getBelongsToManagedRack(), machine.getEnterprise(), dto);
}
return dto;
}
protected void validatePathParameters(final Integer datacenterId, final Integer rackId,
final Integer machineId) throws NotFoundException
{
if (!service.isAssignedTo(datacenterId, rackId, machineId))
{
throw new NotFoundException(APIError.NOT_ASSIGNED_MACHINE_DATACENTER_RACK);
}
}
// Create the persistence object.
public static Machine createPersistenceObject(final MachineDto dto) throws Exception
{
// Set the machine values.
Machine machine = ModelTransformer.persistenceFromTransport(Machine.class, dto);
HypervisorType type = dto.getType();
String ip = dto.getIp();
String ipService = dto.getIpService();
Integer port = dto.getPort();
String user = dto.getUser();
String password = dto.getPassword();
// usused Hypervisor
machine.createHypervisor(type, ip, ipService, port, user, password);
// Set the datastores
for (DatastoreDto datastoreDto : dto.getDatastores().getCollection())
{
machine.getDatastores().add(DatastoreResource.createPersistenceObject(datastoreDto));
}
return machine;
}
public static List<Machine> createPersistenceObjects(final MachinesDto machinesDto)
throws Exception
{
List<Machine> machines = new ArrayList<Machine>();
for (MachineDto machineDto : machinesDto.getCollection())
{
machines.add(createPersistenceObject(machineDto));
}
return machines;
}
public static MachinesDto createTransferObjects(final List<Machine> machinesCreated,
final IRESTBuilder restBuilder) throws Exception
{
MachinesDto machinesDto = new MachinesDto();
for (Machine currentMachine : machinesCreated)
{
machinesDto.getCollection().add(createTransferObject(currentMachine, restBuilder));
}
return machinesDto;
}
/**
* Translates the Node Collector client exception into a {@link WebApplicationException}.
*
* @param e The Exception to transform.
* @return The transformed Exception.
*/
protected APIException translateException(final Exception e)
{
if (e instanceof APIException)
{
return (APIException) e;
}
return new ConflictException(APIError.NODECOLLECTOR_ERROR);
}
}