/** * 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. */ /** * 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 * Boston, MA 02111-1307, USA. */ package com.abiquo.api.services.stub; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import javax.persistence.EntityManager; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.abiquo.api.exceptions.APIError; import com.abiquo.api.services.DefaultApiService; import com.abiquo.api.services.RemoteServiceService; import com.abiquo.commons.amqp.impl.tarantino.domain.DhcpOptionCom; import com.abiquo.commons.amqp.impl.tarantino.domain.DiskDescription; import com.abiquo.commons.amqp.impl.tarantino.domain.DiskDescription.DiskControllerType; import com.abiquo.commons.amqp.impl.tarantino.domain.HypervisorConnection; import com.abiquo.commons.amqp.impl.tarantino.domain.VirtualMachineDefinition.EthernetDriver; import com.abiquo.commons.amqp.impl.tarantino.domain.VirtualMachineDefinition.PrimaryDisk; import com.abiquo.commons.amqp.impl.tarantino.domain.builder.VirtualMachineDescriptionBuilder; import com.abiquo.commons.amqp.impl.tarantino.domain.dto.DatacenterTasks; import com.abiquo.commons.amqp.impl.tarantino.domain.operations.ApplyVirtualMachineStateOp; import com.abiquo.commons.amqp.impl.tarantino.domain.operations.ReconfigureVirtualMachineOp; import com.abiquo.model.enumerator.DiskFormatType; import com.abiquo.model.enumerator.HypervisorType; import com.abiquo.model.enumerator.RemoteServiceType; import com.abiquo.server.core.appslibrary.VirtualImageConversion; import com.abiquo.server.core.appslibrary.VirtualMachineTemplate; import com.abiquo.server.core.cloud.Hypervisor; import com.abiquo.server.core.cloud.VirtualAppliance; import com.abiquo.server.core.cloud.VirtualDatacenter; import com.abiquo.server.core.cloud.VirtualDatacenterRep; import com.abiquo.server.core.cloud.VirtualMachine; import com.abiquo.server.core.cloud.VirtualMachineState; import com.abiquo.server.core.cloud.VirtualMachineStateTransition; import com.abiquo.server.core.infrastructure.InfrastructureRep; import com.abiquo.server.core.infrastructure.RemoteService; import com.abiquo.server.core.infrastructure.network.DhcpOption; import com.abiquo.server.core.infrastructure.network.IpPoolManagement; import com.abiquo.server.core.infrastructure.network.NetworkConfiguration; import com.abiquo.server.core.infrastructure.storage.DiskManagement; import com.abiquo.server.core.infrastructure.storage.StorageRep; /** * Creates tarantino data transfer objects form {@link VirtualMachine} */ @Service // TODO consider not using a singleton. @Scope(value = BeanDefinition.SCOPE_PROTOTYPE) public class TarantinoJobCreator extends DefaultApiService { protected final static Logger logger = LoggerFactory.getLogger(TarantinoJobCreator.class); @Autowired protected StorageRep storageRep; @Autowired protected VirtualDatacenterRep vdcRep; @Autowired protected InfrastructureRep infRep; @Autowired protected RemoteServiceService remoteServiceService; /* ********************************** Be aware of storing any kind of state in this Service */ public TarantinoJobCreator() { } public TarantinoJobCreator(final EntityManager em) { storageRep = new StorageRep(em); vdcRep = new VirtualDatacenterRep(em); infRep = new InfrastructureRep(em); remoteServiceService = new RemoteServiceService(em); } /** * Creates a {@link VirtualMachineDescriptionBuilder} from the current BBDD state. * * @param virtualMachine to mount definition. * @param virtualAppliance, virtual machine context info (app and virtualdatacenter) * @return VirtualMachineDescriptionBuilder to represent the current virtual machine */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public VirtualMachineDescriptionBuilder toTarantinoDto(final VirtualMachine virtualMachine, final VirtualAppliance virtualAppliance, final boolean isHA) { final VirtualDatacenter virtualDatacenter = virtualAppliance.getVirtualDatacenter(); final Integer dcId = virtualDatacenter.getDatacenter().getId(); final VirtualMachineDescriptionBuilder vmDesc = new VirtualMachineDescriptionBuilder(); vmDesc.setBasics(virtualMachine.getUuid(), virtualMachine.getName()); vmDesc.setHA(isHA); logger.debug("Creating disk information"); primaryDiskDefinitionConfiguration(virtualMachine, vmDesc, dcId); logger.debug("Disk information created!"); vmDesc.hardware(virtualMachine.getCpu(), virtualMachine.getRam()); vmDesc.setRdPort(virtualMachine.getVdrpPort()); vmDesc.setRdPassword(virtualMachine.getPassword()); logger.debug("Creating the network related configuration"); addDhcpConfiguration(dcId, vmDesc); vnicDefinitionConfiguration(virtualMachine, vmDesc); logger.debug("Network configuration done!"); logger.debug("Creating the bootstrap configuration"); bootstrapConfiguration(virtualMachine, vmDesc, virtualDatacenter, virtualAppliance); logger.debug("Bootstrap configuration done!"); logger.debug("Configure secondary iSCSI volumes"); secondaryScsiDefinition(virtualMachine, vmDesc); logger.debug("Configure secondary iSCSI done!"); logger.debug("Configure secondary Hard Disks"); secondaryHardDisksDefinition(virtualMachine, vmDesc); logger.debug("Configure secondary Hard Disks done!"); return vmDesc; } public VirtualMachineDescriptionBuilder toTarantinoDto(final VirtualMachine virtualMachine, final VirtualAppliance virtualAppliance) { return toTarantinoDto(virtualMachine, virtualAppliance, false); } /** * Gets the configured DCHP in the datacenter to set its URL in the * {@link com.abiquo.commons.amqp.impl.tarantino.domain.VirtualMachineDefinition.NetworkConfiguration} */ private void addDhcpConfiguration(final Integer datacenterId, final VirtualMachineDescriptionBuilder vmDesc) { // TODO 2.0 will support manual DHCP configuration final RemoteService dhcp = remoteServiceService.getRemoteService(datacenterId, RemoteServiceType.DHCP_SERVICE); if (dhcp == null) { logger.debug("Tarantino Job Creator the datacenter id {} hasn't DHCP service"); // The datacenter hasn't dhcp return; } try { final URI dhcpUri = new URI(dhcp.getUri()); vmDesc.dhcp(dhcpUri.getHost(), dhcpUri.getPort()); } catch (URISyntaxException e) { addConflictErrors(APIError.REMOTE_SERVICE_DHCP_WRONG_URI); flushErrors(); } } /** * Documented in Abiquo EE. * * @param virtualMachine * @param vmDesc void */ protected void secondaryScsiDefinition(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder vmDesc) { // PREMIUM logger.debug("auxDiscsDefinition community implementation"); } /** * Add the secondary hard disks. * * @param virtualMachine virtual machine object. * @param vmDesc definition to send. */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) private void secondaryHardDisksDefinition(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder vmDesc) { List<DiskManagement> hardDisks = virtualMachine.getDisks(); String datastore; if (virtualMachine.getDatastore().getDirectory() != null && !StringUtils.isEmpty(virtualMachine.getDatastore().getDirectory())) { datastore = FilenameUtils.concat(virtualMachine.getDatastore().getRootPath(), virtualMachine .getDatastore().getDirectory()); } else { datastore = virtualMachine.getDatastore().getRootPath(); } DiskControllerType cntrlType = getDiskController(virtualMachine.getHypervisor().getType(), false, false); for (DiskManagement imHard : hardDisks) { vmDesc.addSecondaryHardDisk(imHard.getSizeInMb() * 1048576, imHard.getSequence(), datastore, cntrlType); } } private ApplyVirtualMachineStateOp applyStateVirtualMachineConfiguration( final VirtualMachine virtualMachine, final DatacenterTasks deployTask, final VirtualMachineDescriptionBuilder vmDesc, final HypervisorConnection hypervisorConnection, final VirtualMachineStateTransition stateTransition) { ApplyVirtualMachineStateOp stateJob = new ApplyVirtualMachineStateOp(); stateJob.setVirtualMachine(vmDesc.setBasics(virtualMachine.getUuid(), virtualMachine.getName()).build()); stateJob.setHypervisorConnection(hypervisorConnection); stateJob.setTransaction(com.abiquo.commons.amqp.impl.tarantino.domain.StateTransition .fromValue(stateTransition.name())); stateJob.setId(deployTask.getId() + "." + virtualMachine.getUuid()); return stateJob; } /** * Creates a reconfigure task. The Job id identifies this job and is neede to create the ids of * the items. It is hyerarchic so Task 1 and its job would be 1.1, another 1.2 * * @param vm The virtual machine to reconfigure. * @param originalConfig The original configuration for the virtual machine. * @param newConfig The new configuration for the virtual machine. * @return The reconfigure task. */ public DatacenterTasks reconfigureTask(final VirtualMachine vm, final VirtualMachineDescriptionBuilder originalConfig, final VirtualMachineDescriptionBuilder newConfig) { DatacenterTasks reconfigureTask = new DatacenterTasks(); reconfigureTask.setId(vm.getUuid()); reconfigureTask.setDependent(true); HypervisorConnection hypervisorConnection = hypervisorConnectionConfiguration(vm.getHypervisor()); ReconfigureVirtualMachineOp reconfigureJob = new ReconfigureVirtualMachineOp(); reconfigureJob.setVirtualMachine(originalConfig.setBasics(vm.getUuid(), vm.getName()) .build()); reconfigureJob .setNewVirtualMachine(newConfig.setBasics(vm.getUuid(), vm.getName()).build()); reconfigureJob.setHypervisorConnection(hypervisorConnection); reconfigureJob.setId(reconfigureTask.getId() + "." + vm.getUuid() + "reconfigure"); reconfigureTask.getJobs().add(reconfigureJob); return reconfigureTask; } public HypervisorConnection hypervisorConnectionConfiguration(final Hypervisor hypervisor) { HypervisorConnection hypervisorConnection = new HypervisorConnection(); hypervisorConnection.setHypervisorType(HypervisorConnection.HypervisorType .valueOf(hypervisor.getType().name())); // XXX Dummy implementation // hypervisorConnection.setHypervisorType(HypervisorConnection.HypervisorType.TEST); hypervisorConnection.setIp(hypervisor.getIp()); hypervisorConnection.setLoginPassword(hypervisor.getPassword()); hypervisorConnection.setLoginUser(hypervisor.getUser()); return hypervisorConnection; } /** * In community there are no statful template. If some {@link VirtualImageConversion} attached * use his properties when defining the {@link PrimaryDisk}, else use the * {@link VirtualMachineTemplate} * * @param virtualMachine * @param vmDesc * @param idDatacenter void */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) protected void primaryDiskDefinitionConfiguration(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder vmDesc, final Integer idDatacenter) { String datastore = ""; if (virtualMachine.getDatastore() != null) { if (virtualMachine.getDatastore().getDirectory() != null && !StringUtils.isEmpty(virtualMachine.getDatastore().getDirectory())) { datastore = FilenameUtils.concat(virtualMachine.getDatastore().getRootPath(), virtualMachine.getDatastore().getDirectory()); } else { datastore = virtualMachine.getDatastore().getRootPath(); } } // Repository Manager address List<RemoteService> services = infRep.findRemoteServiceWithTypeInDatacenter(infRep.findById(idDatacenter), RemoteServiceType.APPLIANCE_MANAGER); RemoteService repositoryManager = null; if (!services.isEmpty()) { // Only one remote service of each type by datacenter. repositoryManager = services.get(0); } else { addNotFoundErrors(APIError.NON_EXISTENT_REMOTE_SERVICE_TYPE); flushErrors(); } final VirtualMachineTemplate vmtemplate = virtualMachine.getVirtualMachineTemplate(); final HypervisorType htype = virtualMachine.getHypervisor().getType(); final VirtualImageConversion conversion = virtualMachine.getVirtualImageConversion(); final DiskFormatType format = conversion != null ? conversion.getTargetType() : vmtemplate.getDiskFormatType(); final Long size = conversion != null ? conversion.getSize() : vmtemplate.getDiskFileSize(); final String path = conversion != null ? conversion.getTargetPath() : vmtemplate.getPath(); final DiskControllerType cntrlType = getDiskController(htype, true, false); String url = ""; if (virtualMachine.getVirtualMachineTemplate().getRepository() != null) // repo null when // imported. { url = virtualMachine.getVirtualMachineTemplate().getRepository().getUrl(); } vmDesc.primaryDisk(DiskDescription.DiskFormatType.valueOf(format.name()), size, url, path, datastore, repositoryManager.getUri(), cntrlType); } /** * Only ESXi. Else return null. * <p> * Reads the ''abiquo.esxi.diskController'' properties or use the default: IDE for * non-persistent primary disks and SCSI for aux disk and persistent primary. * * @param hypervisorType, the target hypervisor type * @param isPrimary, primary or secondary disk being added. * @param aux disks always isStateful */ protected DiskControllerType getDiskController(final HypervisorType hypervisorType, final boolean isPrimary, final boolean isStateful) { if (hypervisorType != HypervisorType.VMX_04) { return null; } else { final String primaryOrSecondary = isPrimary ? "primary" : "secondary"; final String controllerProperty = System.getProperty("abiquo.diskController." + primaryOrSecondary); if (!StringUtils.isEmpty(controllerProperty)) { try { return DiskControllerType.valueOf(controllerProperty.toUpperCase()); } catch (Exception e) { logger.error("Invalid ''abiquo.diskController.{}'' property," + "should use IDE/SCSI, but is {}", primaryOrSecondary, controllerProperty); } } if (isStateful) { return DiskControllerType.SCSI; } else { return DiskControllerType.IDE; } } } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) private void vnicDefinitionConfiguration(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder vmDesc) { final EthernetDriver driver = virtualMachine.getEthernetDriverType() != null ? EthernetDriver.valueOf(virtualMachine .getEthernetDriverType().name()) : null; Boolean defaultConfigurationFound = Boolean.FALSE; for (IpPoolManagement i : virtualMachine.getIps()) { List<DhcpOption> dhcplist = i.getVlanNetwork().getDhcpOption(); NetworkConfiguration configuration = i.getVlanNetwork().getConfiguration(); if (i.itHasTheDefaultConfiguration(virtualMachine) && !defaultConfigurationFound) { // This interface is the one that configures the Network parameters. // We force the forward mode to BRIDGED logger.debug("Network configuration with gateway"); vmDesc.addNetwork(i.getMac(), i.getIp(), virtualMachine.getHypervisor() .getMachine().getVirtualSwitch(), i.getNetworkName(), i.getVlanNetwork() .getTag() == null ? 0 : i.getVlanNetwork().getTag(), i.getName(), configuration .getFenceMode(), configuration.getAddress(), configuration.getGateway(), configuration.getNetMask(), configuration.getPrimaryDNS(), configuration .getSecondaryDNS(), configuration.getSufixDNS(), i.getSequence(), toDchpOptionCom(dhcplist), Boolean.TRUE, i.isUnmanagedIp(), driver); defaultConfigurationFound = Boolean.TRUE; continue; } logger.debug("Network configuration without gateway"); // Only the data not related to the network since this data is configured based on the // configureNetwork parameter Integer tag = i.getVlanNetwork().getTag(); vmDesc.addNetwork(i.getMac(), i.getIp(), virtualMachine.getHypervisor().getMachine() .getVirtualSwitch(), i.getNetworkName(), tag, i.getName(), null, null, null, configuration.getNetMask(), null, null, null, i.getSequence(), toDchpOptionCom(dhcplist), Boolean.FALSE, i.isUnmanagedIp(), driver); } } private List<DhcpOptionCom> toDchpOptionCom(final List<DhcpOption> dhcplist) { List<DhcpOptionCom> dhcpComList = new ArrayList<DhcpOptionCom>(); for (DhcpOption d : dhcplist) { DhcpOptionCom dhcp = new DhcpOptionCom(); dhcp.setMask(d.getMask()); dhcp.setOption(d.getOption()); dhcp.setNetworkAddress(d.getNetworkAddress()); dhcp.setGateway(d.getGateway()); dhcp.setNetmask(d.getNetmask()); dhcpComList.add(dhcp); } return dhcpComList; } protected void bootstrapConfiguration(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder vmDesc, final VirtualDatacenter virtualDatacenter, final VirtualAppliance virtualAppliance) { // PREMIUM logger.debug("bootstrap community implementation"); } /** * Creates a deploy task. The Job id identifies this job and is neede to create the ids of the * items. It is hyerarchic so Task 1 and its job would be 1.1, another 1.2 * * @param virtualMachine The virtual machine to reconfigure. * @param builder The original configuration for the virtual machine. * @return The reconfigure task. */ public DatacenterTasks deployTask(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder builder) { DatacenterTasks deployTask = new DatacenterTasks(); deployTask.setId(virtualMachine.getUuid()); deployTask.setDependent(true); HypervisorConnection hypervisorConnection = hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); ApplyVirtualMachineStateOp configuration = applyStateVirtualMachineConfiguration(virtualMachine, deployTask, builder, hypervisorConnection, VirtualMachineStateTransition.CONFIGURE); configuration.setId(deployTask.getId() + ".configure"); ApplyVirtualMachineStateOp state = applyStateVirtualMachineConfiguration(virtualMachine, deployTask, builder, hypervisorConnection, VirtualMachineStateTransition.POWERON); state.setId(deployTask.getId() + ".poweron"); deployTask.getJobs().add(configuration); deployTask.getJobs().add(state); return deployTask; } /** * Creates a undeploy task. The Job id identifies this job and is neede to create the ids of the * items. It is hyerarchic so Task 1 and its job would be 1.1, another 1.2 <br> * <br> * If it is ON we shutdown the virtual machine. * * @param virtualMachine The virtual machine to reconfigure. * @param builder The original configuration for the virtual machine. * @param currentState State of the {@link VirtualMachine} at the start of the undeploy. The * state of this {@link VirtualMachine} at this point is * {@link VirtualMachineState#LOCKED}. * @return The reconfigure task. */ public DatacenterTasks undeployTask(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder builder, final VirtualMachineState currentState) { DatacenterTasks undeployTask = new DatacenterTasks(); undeployTask.setId(virtualMachine.getUuid()); undeployTask.setDependent(true); HypervisorConnection hypervisorConnection = hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); // We must shutdown only if its ON if (VirtualMachineState.ON.equals(currentState)) { ApplyVirtualMachineStateOp state = applyStateVirtualMachineConfiguration(virtualMachine, undeployTask, builder, hypervisorConnection, VirtualMachineStateTransition.POWEROFF); state.setId(undeployTask.getId() + ".poweroff"); undeployTask.getJobs().add(state); } ApplyVirtualMachineStateOp configuration = applyStateVirtualMachineConfiguration(virtualMachine, undeployTask, builder, hypervisorConnection, VirtualMachineStateTransition.DECONFIGURE); configuration.setId(undeployTask.getId() + ".deconfigure"); undeployTask.getJobs().add(configuration); return undeployTask; } /** * Creates a deploy task. The Job id identifies this job and is neede to create the ids of the * items. It is hyerarchic so Task 1 and its job would be 1.1, another 1.2 * * @param virtualMachine The virtual machine to reconfigure. * @param builder The original configuration for the virtual machine. * @return The reconfigure task. */ public DatacenterTasks applyStateTask(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder builder, final VirtualMachineStateTransition machineStateTransition) { DatacenterTasks applyStateTask = new DatacenterTasks(); applyStateTask.setId(virtualMachine.getUuid()); applyStateTask.setDependent(true); HypervisorConnection hypervisorConnection = hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); ApplyVirtualMachineStateOp state = applyStateVirtualMachineConfiguration(virtualMachine, applyStateTask, builder, hypervisorConnection, machineStateTransition); state.setId(applyStateTask.getId() + ".applystate"); applyStateTask.getJobs().add(state); return applyStateTask; } // /** // * Creates a undeploy task. The Job id identifies this job and is neede to create the ids of // the // * items. It is hyerarchic so Task 1 and its job would be 1.1, another 1.2 <br> // * <br> // * If it is ON we shutdown the virtual machine. // * // * @param virtualMachine The virtual machine to reconfigure. // * @param builder The original configuration for the virtual machine. // * @param currentState State of the {@link VirtualMachine} at the start of the undeploy. The // * state of this {@link VirtualMachine} at this point is // * {@link VirtualMachineState#LOCKED}. // * @return The reconfigure task. // */ // public UndeployTaskBuilder undeployAsyncTask(final VirtualMachine virtualMachine, // final VirtualMachineDescriptionBuilder builder, final VirtualMachineState currentState) // { // UndeployTaskBuilder tasksBuilder = new UndeployTaskBuilder(); // // return tasksBuilder.undeployTask(virtualMachine, builder, currentState); // } }