/** * 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.cloud; import static com.abiquo.api.resources.appslibrary.VirtualMachineTemplateResource.VIRTUAL_MACHINE_TEMPLATE; import static com.abiquo.api.util.URIResolver.buildPath; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; import javax.persistence.EntityManager; import javax.ws.rs.core.MultivaluedMap; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.hibernate.Hibernate; 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.exceptions.APIException; import com.abiquo.api.exceptions.BadRequestException; import com.abiquo.api.resources.EnterpriseResource; import com.abiquo.api.resources.EnterprisesResource; import com.abiquo.api.resources.TaskResourceUtils; import com.abiquo.api.resources.appslibrary.DatacenterRepositoriesResource; import com.abiquo.api.resources.appslibrary.DatacenterRepositoryResource; import com.abiquo.api.resources.appslibrary.VirtualMachineTemplateResource; import com.abiquo.api.resources.appslibrary.VirtualMachineTemplatesResource; import com.abiquo.api.resources.cloud.DiskResource; import com.abiquo.api.resources.cloud.DisksResource; import com.abiquo.api.resources.cloud.IpAddressesResource; import com.abiquo.api.resources.cloud.PrivateNetworkResource; import com.abiquo.api.resources.cloud.PrivateNetworksResource; import com.abiquo.api.resources.cloud.VirtualApplianceResource; import com.abiquo.api.resources.cloud.VirtualAppliancesResource; import com.abiquo.api.resources.cloud.VirtualDatacenterResource; import com.abiquo.api.resources.cloud.VirtualDatacentersResource; import com.abiquo.api.resources.cloud.VirtualMachineNetworkConfigurationResource; import com.abiquo.api.resources.cloud.VirtualMachineResource; import com.abiquo.api.resources.cloud.VirtualMachinesResource; import com.abiquo.api.services.DefaultApiService; import com.abiquo.api.services.NetworkService; import com.abiquo.api.services.RemoteServiceService; import com.abiquo.api.services.UserService; import com.abiquo.api.services.VirtualMachineAllocatorService; import com.abiquo.api.services.stub.AMServiceStub; import com.abiquo.api.services.stub.TarantinoJobCreator; import com.abiquo.api.services.stub.TarantinoService; import com.abiquo.api.tracer.TracerLogger; import com.abiquo.api.util.URIResolver; import com.abiquo.api.util.snapshot.SnapshotUtils.SnapshotType; import com.abiquo.commons.amqp.impl.tarantino.domain.builder.VirtualMachineDescriptionBuilder; import com.abiquo.model.enumerator.EthernetDriverType; import com.abiquo.model.enumerator.HypervisorType; import com.abiquo.model.enumerator.NetworkType; import com.abiquo.model.rest.RESTLink; import com.abiquo.model.transport.SingleResourceTransportDto; import com.abiquo.model.transport.error.CommonError; import com.abiquo.model.transport.error.ErrorDto; import com.abiquo.model.transport.error.ErrorsDto; import com.abiquo.model.util.ModelTransformer; import com.abiquo.scheduler.SchedulerLock; import com.abiquo.scheduler.VirtualMachineRequirementsFactory; import com.abiquo.server.core.appslibrary.AppsLibraryRep; import com.abiquo.server.core.appslibrary.VirtualMachineTemplate; import com.abiquo.server.core.cloud.Hypervisor; import com.abiquo.server.core.cloud.NodeVirtualImage; import com.abiquo.server.core.cloud.VirtualAppliance; import com.abiquo.server.core.cloud.VirtualApplianceRep; 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.VirtualMachine.OrderByEnum; import com.abiquo.server.core.cloud.VirtualMachineDto; import com.abiquo.server.core.cloud.VirtualMachineRep; import com.abiquo.server.core.cloud.VirtualMachineState; import com.abiquo.server.core.cloud.VirtualMachineStateTransition; import com.abiquo.server.core.cloud.VirtualMachineWithNodeDto; import com.abiquo.server.core.enterprise.DatacenterLimits; import com.abiquo.server.core.enterprise.Enterprise; import com.abiquo.server.core.enterprise.EnterpriseRep; import com.abiquo.server.core.enterprise.User; import com.abiquo.server.core.infrastructure.Datacenter; import com.abiquo.server.core.infrastructure.InfrastructureRep; import com.abiquo.server.core.infrastructure.RemoteService; import com.abiquo.server.core.infrastructure.management.Rasd; import com.abiquo.server.core.infrastructure.management.RasdDAO; import com.abiquo.server.core.infrastructure.management.RasdManagement; import com.abiquo.server.core.infrastructure.management.RasdManagementDAO; import com.abiquo.server.core.infrastructure.network.IpPoolManagement; import com.abiquo.server.core.infrastructure.network.IpPoolManagementDAO; import com.abiquo.server.core.infrastructure.network.NetworkAssignment; import com.abiquo.server.core.infrastructure.network.NetworkAssignmentDAO; import com.abiquo.server.core.infrastructure.network.NetworkConfiguration; import com.abiquo.server.core.infrastructure.network.VLANNetwork; import com.abiquo.server.core.infrastructure.network.VLANNetworkDAO; import com.abiquo.server.core.infrastructure.storage.DiskManagement; import com.abiquo.server.core.infrastructure.storage.StorageRep; import com.abiquo.server.core.infrastructure.storage.VolumeManagement; import com.abiquo.server.core.scheduler.VirtualMachineRequirements; import com.abiquo.server.core.task.Task; import com.abiquo.server.core.util.network.IPNetworkRang; import com.abiquo.tracer.ComponentType; import com.abiquo.tracer.EventType; import com.abiquo.tracer.SeverityType; @Service @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public class VirtualMachineService extends DefaultApiService { /** The logger object **/ private final static Logger LOGGER = LoggerFactory.getLogger(VirtualMachineService.class); @Autowired protected VirtualMachineRep repo; @Autowired protected RasdManagementDAO rasdDao; @Autowired protected RasdDAO rasdRawRao; @Autowired protected VirtualApplianceRep vappRep; @Autowired protected VirtualDatacenterRep vdcRep; @Autowired private RemoteServiceService remoteServiceService; @Autowired private UserService userService; @Autowired protected EnterpriseRep enterpriseRep; @Autowired private StorageRep storageRep; @Autowired private VirtualMachineAllocatorService vmAllocatorService; @Autowired private VirtualMachineRequirementsFactory vmRequirements; @Autowired private VirtualDatacenterService vdcService; @Autowired private InfrastructureRep infRep; @Autowired protected AppsLibraryRep appsLibRep; @Autowired protected TarantinoService tarantino; @Autowired private TarantinoJobCreator jobCreator; @Autowired private NetworkService ipService; @Autowired private AMServiceStub amService; @Autowired private IpPoolManagementDAO ipPoolManDao; @Autowired private NetworkAssignmentDAO netAssignDao; @Autowired private VLANNetworkDAO vlanNetworkDao; public VirtualMachineService() { } public VirtualMachineService(final EntityManager em) { this.repo = new VirtualMachineRep(em); this.rasdDao = new RasdManagementDAO(em); this.vappRep = new VirtualApplianceRep(em); this.vdcRep = new VirtualDatacenterRep(em); this.remoteServiceService = new RemoteServiceService(em); this.userService = new UserService(em); this.enterpriseRep = new EnterpriseRep(em); this.vmAllocatorService = new VirtualMachineAllocatorService(em); this.vmRequirements = new VirtualMachineRequirementsFactory(); // XXX this.vdcService = new VirtualDatacenterService(em); this.infRep = new InfrastructureRep(em); this.appsLibRep = new AppsLibraryRep(em); this.tarantino = new TarantinoService(em); this.storageRep = new StorageRep(em); this.jobCreator = new TarantinoJobCreator(em); this.ipService = new NetworkService(em); this.ipPoolManDao = new IpPoolManagementDAO(em); this.vlanNetworkDao = new VLANNetworkDAO(em); this.tracer = new TracerLogger(); } public Collection<VirtualMachine> findByHypervisor(final Hypervisor hypervisor) { return repo.findByHypervisor(hypervisor); } public Collection<VirtualMachine> findNotAllocated(final Hypervisor hypervisor) { assert hypervisor != null; return repo.findVirtualMachinesNotAllocatedCompatibleHypervisor(hypervisor); } public Collection<VirtualMachine> findByEnterprise(final Enterprise enterprise) { assert enterprise != null; return repo.findByEnterprise(enterprise); } public List<VirtualMachine> findVirtualMachinesByUser(final Enterprise enterprise, final User user) { return repo.findVirtualMachinesByUser(enterprise, user); } public List<VirtualMachine> findByVirtualAppliance(final VirtualAppliance vapp, final Integer startwith, final String orderBy, final String filter, final Integer limit, final Boolean descOrAsc) { // Check if the orderBy element is actually one of the available ones VirtualMachine.OrderByEnum orderByEnum = VirtualMachine.OrderByEnum.fromValue(orderBy); if (orderByEnum == null) { LOGGER .info("Bad parameter 'by' in request to get the virtual machines by virtual appliance."); addValidationErrors(APIError.QUERY_INVALID_PARAMETER); flushErrors(); } return repo.findVirtualMachinesByVirtualAppliance(vapp.getId(), startwith, limit, filter, orderByEnum, descOrAsc); } public VirtualMachine findByUUID(final String uuid) { return repo.findByUUID(uuid); } public VirtualMachine findByName(final String name) { return repo.findByName(name); } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public VirtualMachine getVirtualMachine(final Integer vdcId, final Integer vappId, final Integer vmId) { VirtualMachine vm = repo.findVirtualMachineById(vmId); VirtualAppliance vapp = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); VirtualDatacenter vdc = vdcRep.findById(vdcId); if (vm == null || !isAssignedTo(vmId, vapp.getId())) { LOGGER.error("Error retrieving the virtual machine: {} does not exist", vmId); addNotFoundErrors(APIError.NON_EXISTENT_VIRTUALMACHINE); flushErrors(); } LOGGER.debug("Virtual machine {} found", vmId); // if the ips are external, we need to set the limitID in order to return the // proper info. for (IpPoolManagement ip : vm.getIps()) { if (ip.getVlanNetwork().getEnterprise() != null) { // needed for REST links. DatacenterLimits dl = infRep.findDatacenterLimits(ip.getVlanNetwork().getEnterprise(), vdc.getDatacenter()); ip.getVlanNetwork().setLimitId(dl.getId()); } } return vm; } /** * This method is semi-duplicated from VirtualApplianceService, but bean can not be used due * cross references */ private VirtualAppliance getVirtualApplianceAndCheckVirtualDatacenter(final Integer vdcId, final Integer vappId) { // checks vdc exist vdcService.getVirtualDatacenter(vdcId); VirtualAppliance vapp = vappRep.findById(vappId); if (vapp == null || !vapp.getVirtualDatacenter().getId().equals(vdcId)) { addNotFoundErrors(APIError.NON_EXISTENT_VIRTUALAPPLIANCE); flushErrors(); } return vapp; } public VirtualMachine getVirtualMachine(final Integer vmId) { return repo.findVirtualMachineById(vmId); } public VirtualMachine getVirtualMachineByHypervisor(final Hypervisor hyp, final Integer vmId) { VirtualMachine vm = repo.findVirtualMachineByHypervisor(hyp, vmId); if (vm == null) { addNotFoundErrors(APIError.NON_EXISTENT_VIRTUALMACHINE); flushErrors(); } return vm; } @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) public void addImportedVirtualMachine(final VirtualMachine virtualMachine) { validate(virtualMachine); repo.insert(virtualMachine); } /** * Gets the DTO object and validates all of its parameters. Prepares the {@link VirtualMachine} object * and sends the object to the method {@link VirtualMachineService#reconfigureVirtualMachine(VirtualDatacenter, VirtualAppliance, VirtualMachine, VirtualMachine). * * @param vdcId identifier of the {@link VirtualDatacenter} * @param vappId identifier of the {@link VirtualAppliance} * @param vmId identifier of the {@link VirtualMachine} * @param forceSoftLimits * @param dto input {@link VirtualMachineDto} object with all its links. * @return the link to the asnyncronous task. */ @Transactional(propagation = Propagation.REQUIRED) public String reconfigureVirtualMachine(final Integer vdcId, final Integer vappId, final Integer vmId, final VirtualMachineDto dto, final VirtualMachineState originalState, final Boolean forceSoftLimits) { VirtualDatacenter vdc = getVirtualDatacenter(vdcId); // We need to operate with concrete and this also check that the VirtualMachine belongs to // those VirtualAppliance and VirtualDatacenter VirtualMachine virtualMachine = getVirtualMachine(vdcId, vappId, vmId); VirtualAppliance virtualAppliance = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); VirtualMachine newvm = buildVirtualMachineFromDto(vdc, virtualAppliance, dto); // the provided VM Dto doesn't have the conversion link (is itn't published already) // so the created object form Dto needs to set the same conversion newvm.setVirtualImageConversion(virtualMachine.getVirtualImageConversion()); newvm.setTemporal(virtualMachine.getId()); // we set the id to temporal since we are trying // we need to get the configuration value ALWAYS after to set the ips of the new virtual // machine // since it depends to it to check if the configuration of the network is valid // And also we need to set AFTER to set the 'temporal' value from this DTO. newvm.setNetworkConfiguration(getNetworkConfigurationFromDto(virtualAppliance, newvm, dto)); // to update the virtualMachine. // allocated resources not present in the requested reconfiguration newvm.setDatastore(virtualMachine.getDatastore()); newvm.setHypervisor(virtualMachine.getHypervisor()); return reconfigureVirtualMachine(vdc, virtualAppliance, virtualMachine, newvm, originalState, forceSoftLimits); } /** * updates the {@link NodeVirtualImage} name, y and x (those setted in the virtual appliance * builder. <br> * This method must persist the changes even if the reconfigure of the {@link VirtualMachine} * fails. * * @param vdcId identifier of the {@link VirtualDatacenter} * @param vappId identifier of the {@link VirtualAppliance} * @param vmId identifier of the {@link VirtualMachine} * @param dto input {@link VirtualMachineDto} object with all its links. * @return the link to the asnyncronous task. */ @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateNodeVirtualImageInfo(final Integer vdcId, final Integer vappId, final Integer vmId, final VirtualMachineWithNodeDto dto) { NodeVirtualImage nodeVirtualImage = getNodeVirtualImage(vdcId, vappId, vmId); nodeVirtualImage.setName(dto.getNodeName()); nodeVirtualImage.setY(dto.getY()); nodeVirtualImage.setX(dto.getX()); } /** * <pre> * Prepare the machine to reconfigure. That means: * - Check the new allocation requirements * - Create the temporal register in database with the old values of the virtual machine for rollback purposes. * - Prepares and send the tarantino job. * - Returns the link to the asynchronous task to query in order to see the progress. * </pre> * * This method assumes: - Any of the input params is null. - The 'isAssigned' checks are already * done: the virtual machine actually belongs to virtual appliance and the virtual appliance * actually belongs to virtual datacenter. * * @param vdc {@link VirtualDatacenter} object where the virtual machine to reconfigure belongs * to. * @param vapp {@link VirtualAppliance} object where the virtual machine to reconfigure belongs * to. * @param newValues {@link VirtualMachine} exactly as we want to be after the reconfigure. * @param forceSoftLimits param if true does not trace the soft limit * @return a String containing the URI where to check the progress. */ @Transactional(propagation = Propagation.REQUIRED) public String reconfigureVirtualMachine(final VirtualDatacenter vdc, final VirtualAppliance vapp, final VirtualMachine vm, final VirtualMachine newValues, final VirtualMachineState originalState) { return reconfigureVirtualMachine(vdc, vapp, vm, newValues, originalState, false); } public String reconfigureVirtualMachine(final VirtualDatacenter vdc, final VirtualAppliance vapp, final VirtualMachine vm, final VirtualMachine newValues, final VirtualMachineState originalState, final Boolean forceSoftLimits) { if (checkReconfigureTemplate(vm.getVirtualMachineTemplate(), newValues.getVirtualMachineTemplate())) { if (vm.isCaptured()) { // don't allow to change the template if the machine is capture addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE); flushErrors(); } LOGGER.debug("Will reconfigure the vm template"); if (originalState.existsInHypervisor()) { addConflictErrors(APIError.VIRTUAL_MACHINE_RECONFIGURE_TEMPLATE_IN_THE_HYPERVISOR); flushErrors(); } // already checked is not attached if (newValues.getVirtualMachineTemplate().isStateful()) { LOGGER.debug("Attaching virtual machine template volume"); newValues.getVirtualMachineTemplate().getVolume().attach(0, vm); newValues.getVirtualMachineTemplate().getVolume().setVirtualAppliance(vapp); // primary disk sequence == 0 } } LOGGER.debug("Starting the reconfigure of the virtual machine {}", vm.getId()); LOGGER.debug("Check for permissions"); // The user must have the proper permission userService.checkCurrentEnterpriseForPostMethods(vm.getEnterprise()); newValues.setEnterprise(vm.getEnterprise()); LOGGER.debug("Permission granted"); VirtualMachine backUpVm = null; VirtualMachineDescriptionBuilder virtualMachineTarantino = null; // if NOT_ALLOCATED isn't necessary to check the resource limits and // insert the 'backup' resources try { if (originalState == VirtualMachineState.OFF) { // There might be different hardware needs. This call also recalculate. LOGGER .debug("Updating the hardware needs in DB for virtual machine {}", vm.getId()); VirtualMachineRequirements requirements = vmRequirements.createVirtualMachineRequirements(vm, newValues); vmAllocatorService.checkAllocate(vapp.getId(), newValues, requirements, forceSoftLimits); LOGGER .debug("Creating the temporary register in Virtual Machine for rollback purposes"); backUpVm = createBackUpMachine(vm); repo.insert(backUpVm); createBackUpResources(vm, backUpVm); insertBackUpResources(backUpVm); LOGGER.debug("Rollback register has id {}" + vm.getId()); // Before to update the virtualmachine to new values, create the tarantino // descriptor // (only if the VM is deployed and OFF, othwerwise it won't have a datastore) virtualMachineTarantino = jobCreator.toTarantinoDto(vm, vapp); } // update the old virtual machine with the new virtual machine values. // and set the ID of the backupmachine (which has the old values) for recovery purposes. LOGGER.debug("Updating the virtual machine in the DB with id {}", vm.getId()); updateVirtualMachineToNewValues(vapp, vm, newValues); LOGGER.debug("Updated virtual machine {}", vm.getId()); // it is required a tarantino Task ? if (originalState == VirtualMachineState.NOT_ALLOCATED) { return null; } LOGGER.debug("Checking requires add initiatorMappings"); initiatorMappings(vm); // refresh the virtualmachine object with the new values to get the // correct resources. VirtualMachineDescriptionBuilder newVirtualMachineTarantino = jobCreator.toTarantinoDto(newValues, vapp); // A datacenter task is a set of jobs and datacenter task. This is, the deploy of a // VirtualMachine is the definition of the VirtualMachine and the job, power on return tarantino.reconfigureVirtualMachine(vm, virtualMachineTarantino, newVirtualMachineTarantino); } catch (APIException e) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_RECONFIGURE, "virtualMachine.reconfigureError", vm.getName()); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_RECONFIGURE, e, "virtualMachine.reconfigureError", vm.getName()); throw e; } catch (Exception ex) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_RECONFIGURE, "virtualMachine.reconfigureError", vm.getName()); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_RECONFIGURE, ex, "virtualMachine.reconfigureError", vm.getName()); addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); return null; } } /** * Checks if the {@link VirtualMachineTemplate} is being changed, if so checks the new template * is an instance or a persistent of the original template (if not reports a conflict * {@link APIError}). * * @return true if the {@link VirtualMachineTemplate} is being reconfigured. */ protected boolean checkReconfigureTemplate(final VirtualMachineTemplate original, final VirtualMachineTemplate requested) { if (original.getId().equals(requested.getId())) { return false; } else if (!original.isManaged()) { addConflictErrors(APIError.VIRTUAL_MACHINE_RECONFIGURE_NOT_MANAGED); flushErrors(); } else if (!requested.isManaged()) { addConflictErrors(APIError.VIRTUAL_MACHINE_RECONFIGURE_TEMPLATE_NOT_MANAGED); flushErrors(); } else if (requested.isStateful() && requested.getVolume().isAttached()) { addConflictErrors(APIError.VIRTUAL_MACHINE_RECONFIGURE_TEMPLATE_ATTACHED_PRESISTENT); flushErrors(); } else if (original.isMaster() && !requested.isMaster() && !requested.getMaster().getId().equals(original.getId())) { addConflictErrors(APIError.VIRTUAL_MACHINE_RECONFIGURE_TEMPLATE_NOT_SAME_MASTER); flushErrors(); } else if (!original.isMaster() && !requested.isMaster() && !requested.getMaster().getId().equals(original.getMaster().getId())) { addConflictErrors(APIError.VIRTUAL_MACHINE_RECONFIGURE_TEMPLATE_NOT_SAME_MASTER); flushErrors(); } else if (requested.isMaster() && !original.isMaster() && !requested.getId().equals(original.getMaster().getId())) { addConflictErrors(APIError.VIRTUAL_MACHINE_RECONFIGURE_TEMPLATE_NOT_SAME_MASTER); flushErrors(); } else if (original.isMaster() && requested.isMaster()) { addConflictErrors(APIError.VIRTUAL_MACHINE_RECONFIGURE_TEMPLATE_NOT_SAME_MASTER); flushErrors(); } return true; } /** * Insert the backup resources in database. * * @param backUpVm */ private void insertBackUpResources(final VirtualMachine backUpVm) { for (IpPoolManagement ip : backUpVm.getIps()) { vdcRep.insertTemporalIpManagement(ip); } for (VolumeManagement vol : backUpVm.getVolumes()) { storageRep.insertTemporalVolume(vol); } for (DiskManagement disk : backUpVm.getDisks()) { storageRep.insertTemporalHardDisk(disk); } // XXX this a kind of magic !!! backUpVm.setIps(null); backUpVm.setVolumes(null); backUpVm.setDisks(null); repo.update(backUpVm); } /** * Just assign the new virtual machine values to the new ones. * * @param old old virtual machine instance * @param vmnew new virtual machine values */ private void updateVirtualMachineToNewValues(final VirtualAppliance vapp, final VirtualMachine old, final VirtualMachine vmnew) { // if client changes cpu or ram, can be changed in captured machines // only if hypervisor is ESXi if (old.getCpu() != vmnew.getCpu() || old.getRam() != vmnew.getRam()) { if (old.isCaptured() && !vapp.getVirtualDatacenter().getHypervisorType().equals(HypervisorType.VMX_04)) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE); flushErrors(); } } // the rest of values never can be changed when reconfigure. if (differentDescription(old, vmnew) || differentNetworkConfiguration(old, vmnew) || differentPassword(old, vmnew)) { if (old.isCaptured() && !vapp.getVirtualDatacenter().getHypervisorType().equals(HypervisorType.VMX_04)) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE); flushErrors(); } else if (old.isCaptured() && vapp.getVirtualDatacenter().getHypervisorType().equals(HypervisorType.VMX_04)) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE_FULLY); flushErrors(); } } old.setCpu(vmnew.getCpu()); old.setDescription(vmnew.getDescription()); old.setRam(vmnew.getRam()); old.setNetworkConfiguration(vmnew.getNetworkConfiguration()); old.setPassword(vmnew.getPassword()); old.setVirtualMachineTemplate(vmnew.getVirtualMachineTemplate()); List<Integer> usedNICslots = dellocateOldNICs(old, vmnew); // if the number of old nics still used is different from // the number of usedNICslots, OR the number of old nics is different // from the new ones, it means some NICs has changed. if (usedNICslots.size() != old.getIps().size() || old.getIps().size() != vmnew.getIps().size()) { if (old.isCaptured() && !vapp.getVirtualDatacenter().getHypervisorType().equals(HypervisorType.VMX_04)) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE); flushErrors(); } else if (old.isCaptured() && vapp.getVirtualDatacenter().getHypervisorType().equals(HypervisorType.VMX_04)) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE_FULLY); flushErrors(); } } allocateNewNICs(vapp, old, vmnew.getIps(), usedNICslots); List<Integer> usedVolumeSlots = dellocateOldVolumes(old, vmnew); // if the number of old volumes still used is different from // the number of usedvolume Slots, OR the number of old volumes is different // from the new ones, it means some Volumes has changed. if (usedVolumeSlots.size() != old.getVolumes().size() || old.getVolumes().size() != vmnew.getVolumes().size()) { if (old.isCaptured() && !vapp.getVirtualDatacenter().getHypervisorType().equals(HypervisorType.VMX_04)) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE); flushErrors(); } else if (old.isCaptured() && vapp.getVirtualDatacenter().getHypervisorType().equals(HypervisorType.VMX_04)) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE_FULLY); flushErrors(); } } // never use the slot 0 for storage since it is the virtual image List<Integer> usedStorageSlots = dellocateOldDisks(old, vmnew); // if the number of old hard disks still used is different from // the number of usedhard Slots, OR the number of old storage is different // from the new ones, it means some hard disk has changed. if (usedStorageSlots.size() != old.getDisks().size() || old.getDisks().size() != vmnew.getDisks().size()) { if (old.isCaptured() && !vapp.getVirtualDatacenter().getHypervisorType().equals(HypervisorType.VMX_04)) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE); flushErrors(); } else if (old.isCaptured() && vapp.getVirtualDatacenter().getHypervisorType().equals(HypervisorType.VMX_04)) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_CAN_NOT_RECONFIGURE_FULLY); flushErrors(); } } usedStorageSlots.addAll(usedVolumeSlots); List<RasdManagement> storageResources = new ArrayList<RasdManagement>(); storageResources.addAll(vmnew.getDisks()); storageResources.addAll(vmnew.getVolumes()); allocateNewStorages(vapp, old, storageResources, usedStorageSlots); repo.update(old); // FIXME: improvement related ABICLOUDPREMIUM-2925 updateNodeVirtualImage(old, vmnew.getVirtualMachineTemplate()); } private boolean differentNetworkConfiguration(final VirtualMachine old, final VirtualMachine vmnew) { if (old.getNetworkConfiguration() == null && vmnew.getNetworkConfiguration() == null) { return false; } else if (old.getNetworkConfiguration() == null && vmnew.getNetworkConfiguration() != null) { return true; } else if (old.getNetworkConfiguration() != null && vmnew.getNetworkConfiguration() == null) { return true; } else { return !old.getNetworkConfiguration().getId() .equals(vmnew.getNetworkConfiguration().getId()); } } /** * Check if the password has changed. * * @param old * @param vmnew * @return */ private boolean differentPassword(final VirtualMachine old, final VirtualMachine vmnew) { if (vmnew.getPassword() != null && vmnew.getPassword().isEmpty()) { vmnew.setPassword(null); } if (old.getPassword() == null && vmnew.getPassword() == null) { return false; } else if (old.getPassword() == null && vmnew.getPassword() != null) { return true; } else if (old.getPassword() != null && vmnew.getPassword() == null) { return true; } else { return !vmnew.getPassword().equals(old.getPassword()); } } /** * Check if the description has changed. * * @param old * @param vmnew * @return */ private boolean differentDescription(final VirtualMachine old, final VirtualMachine vmnew) { if (old.getDescription() == null && vmnew.getDescription() == null) { return false; } else if (old.getDescription() == null && vmnew.getDescription() != null) { return true; } else if (old.getDescription() != null && vmnew.getDescription() == null) { return true; } else { return !old.getDescription().equals(vmnew.getDescription()); } } /** * updates the virtual machine template from node virtual image with the template given by the * {@link VirtualMachineTemplate} param. <br> * This method must persist the changes even if the reconfigure of the {@link VirtualMachine} * fails. * * @param vm {@link VirtualMachine} Virtual machine where obtains the related * {@link NodeVirtualImage} * @parem template {@link VirtualMachineTemplate} Virtual Machine Template to set */ @Transactional(propagation = Propagation.REQUIRES_NEW) private void updateNodeVirtualImage(final VirtualMachine vm, final VirtualMachineTemplate template) { NodeVirtualImage nvi = repo.findNodeVirtualImageByVm(vm); nvi.setVirtualImage(template); repo.updateNodeVirtualImage(nvi); } /** * Check if the resource is into the list of new resources. * * @param resource {@link RasdManagement} resource to check * @param resources list of new Resources of the machine * @return true if the resource is into the new list. */ private boolean resourceIntoNewList(final RasdManagement resource, final List< ? extends RasdManagement> newResources) { for (RasdManagement newResource : newResources) { // Since the values point to the same rasd, the id should be the same if (resource.getRasd().getId().equals(newResource.getRasd().getId())) { return true; } } return false; } @Transactional(propagation = Propagation.REQUIRED) public void updateVirtualMachine(final VirtualMachine vm) { userService.checkCurrentEnterpriseForPostMethods(vm.getEnterprise()); repo.update(vm); } /** * Check if a virtual machine is defined into a virtual appliance. * * @param vmId identifier of the virtual machine * @param vappId identifier of the virtual appliance * @return True if it is, false otherwise. */ public boolean isAssignedTo(final Integer vmId, final Integer vappId) { List<VirtualMachine> vms = repo.findVirtualMachinesByVirtualAppliance(vappId, 0, 0, "", OrderByEnum.NAME, true); for (VirtualMachine vm : vms) { if (vm.getId().equals(vmId)) { return true; } } return false; } public VirtualMachineStateTransition validMachineStateChange( final VirtualMachine virtualMachine, final VirtualMachineState newState) { VirtualMachineStateTransition validTransition = VirtualMachineStateTransition.getValidVmStateChangeTransition( virtualMachine.getState(), newState); if (validTransition == null) { addConflictErrors(APIError.VIRTUAL_MACHINE_STATE_CHANGE_ERROR); flushErrors(); } return validTransition; } /** * Compare the state of vm with the state passed through parameter * * @param vm VirtualMachine to which compare the state * @param state a valid VirtualMachine state * @return true if its the same state, false otherwise */ public Boolean sameState(final VirtualMachine vm, final VirtualMachineState state) { String actual = vm.getState().toOVF();// OVFGeneratorService.getActualState(vm); return state.toOVF().equalsIgnoreCase(actual); } /** * Delete a {@link VirtualMachine}. And the {@link VirtualMachineNode}. * * @param virtualMachine to delete. void */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void deleteVirtualMachine(final Integer vmId, final Integer vappId, final Integer vdcId, final VirtualMachineState originalState) { // We need to operate with concrete and this also check that the VirtualMachine belongs // to those VirtualAppliance and VirtualDatacenter VirtualMachine virtualMachine = getVirtualMachine(vdcId, vappId, vmId); // The user must have the proper permission userService.checkCurrentEnterpriseForPostMethods(virtualMachine.getEnterprise()); if (VirtualMachineState.ALLOCATED.equals(originalState)) { final String lockMsg = "Deallocate " + virtualMachine.getId(); try { LOGGER .warn("Delete of the ALLOCATED virtualMachine that has resources allocated. Deallocating"); SchedulerLock.acquire(lockMsg); vmAllocatorService.deallocateVirtualMachine(virtualMachine); } finally { SchedulerLock.release(lockMsg); LOGGER .warn("Delete of the ALLOCATED virtualMachine that has resources allocated. Deallocating successful"); } } else if (originalState.existsInHypervisor()) { LOGGER.debug("Delete of the virtualMachine that exists in hypervisor"); undeployVirtualMachineAndDelete(virtualMachine, vappId, vdcId, originalState); return; } LOGGER.debug("Deleting the virtual machine with UUID {}", virtualMachine.getUuid()); NodeVirtualImage nodeVirtualImage = findNodeVirtualImage(virtualMachine); LOGGER.trace("Deleting the node virtual image with id {}", nodeVirtualImage.getId()); repo.deleteNodeVirtualImage(nodeVirtualImage); LOGGER.trace("Deleted node virtual image!"); repo.deleteVirtualMachine(virtualMachine); // Does it has volumes? PREMIUM detachVolumesFromVirtualMachine(virtualMachine); LOGGER.debug("Detached the virtual machine's volumes with UUID {}", virtualMachine.getUuid()); detachVirtualMachineIPs(virtualMachine); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_DELETE, "virtualMachine.delete"); } /** * Deletes the {@link Rasd} of an {@link IpPoolManagement}. * * @param virtualMachine void */ public void detachVirtualMachineIPs(final VirtualMachine virtualMachine) { for (IpPoolManagement ip : virtualMachine.getIps()) { vdcRep.deleteRasd(ip.getRasd()); switch (ip.getType()) { case UNMANAGED: rasdDao.remove(ip); break; case EXTERNAL: ip.setVirtualDatacenter(null); ip.setMac(null); ip.setName(null); default: ip.detach(); } } } /** * Delete a {@link VirtualMachine}. And the {@link VirtualMachineNode}. Without account for * permission. * * @param virtualMachine to delete. void */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void deleteVirtualMachineBySystem(final VirtualMachine virtualMachine) { LOGGER.debug("Deleting the virtual machine with UUID {}", virtualMachine.getUuid()); NodeVirtualImage nodeVirtualImage = findNodeVirtualImage(virtualMachine); LOGGER.trace("Deleting the node virtual image with id {}", nodeVirtualImage.getId()); repo.deleteNodeVirtualImage(nodeVirtualImage); LOGGER.trace("Deleted node virtual image!"); repo.deleteVirtualMachine(virtualMachine); // Does it has volumes? PREMIUM detachVolumesFromVirtualMachine(virtualMachine); LOGGER.debug("Detached the virtual machine's volumes with UUID {}", virtualMachine.getUuid()); detachVirtualMachineIPs(virtualMachine); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_DELETE, "virtualMachine.delete"); } /** * This method is properly documented in the premium edition. * * @param virtualMachine void */ public void detachVolumesFromVirtualMachine(final VirtualMachine virtualMachine) { // PREMIUM } /** * Persists a {@link VirtualMachine}. If the preconditions are met. * * @param virtualMachine to create. void */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public VirtualMachine createVirtualMachine(final Integer vdcId, final Integer vappId, final VirtualMachineDto dto) { VirtualDatacenter vdc = getVirtualDatacenter(vdcId); // We need to operate with concrete and this also check that the VirtualMachine belongs to // those VirtualAppliance and VirtualDatacenter VirtualAppliance virtualAppliance = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); // First we get from dto. All the values wi VirtualMachine virtualMachine = buildVirtualMachineFromDto(vdc, virtualAppliance, dto); virtualMachine.setUuid(UUID.randomUUID().toString()); virtualMachine.setIdType(VirtualMachine.MANAGED); String nodeName = virtualMachine.getName(); if (dto instanceof VirtualMachineWithNodeDto) { nodeName = ((VirtualMachineWithNodeDto) dto).getNodeName();// we use the name to create // the node } virtualMachine.setName("ABQ_" + virtualMachine.getUuid()); // Set the user and enterprise virtualMachine.setUser(userService.getCurrentUser()); virtualMachine.setEnterprise(userService.getCurrentUser().getEnterprise()); // We check for a suitable conversion (PREMIUM) attachVirtualMachineTemplateConversion(virtualAppliance.getVirtualDatacenter(), virtualMachine); // Attach the matching stateful volume if the template is a saved persistent template if (virtualMachine.getVirtualMachineTemplate().isStateful()) { LOGGER.debug("Attaching virtual machine template volume"); virtualMachine.getVirtualMachineTemplate().getVolume() .attach(0, virtualMachine, virtualAppliance); virtualMachine.getVirtualMachineTemplate().getVolume() .setVirtualAppliance(virtualAppliance); virtualMachine.getVirtualMachineTemplate().getVolume() .setVirtualMachine(virtualMachine); } // At this stage the virtual machine is not associated with any hypervisor virtualMachine.setState(VirtualMachineState.NOT_ALLOCATED); // A user can only create virtual machine validate(virtualMachine); repo.createVirtualMachine(virtualMachine); // The entity that defines the relation between a virtual machine, virtual applicance and // virtual machine template is VirtualImageNode createNodeVirtualImage(virtualMachine, virtualAppliance, StringUtils.isBlank(nodeName) ? virtualMachine.getVirtualMachineTemplate().getName() : nodeName); // We must add the default NIC. This is the very next free IP in the virtual datacenter's // default VLAN ipService.assignDefaultNICToVirtualMachine(virtualMachine.getId()); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_CREATE, "virtualMachine.create"); return virtualMachine; } /** * @param vdcId * @param vappId * @param dto * @return */ public VirtualMachine modifyVirtualMachine(final Integer vdcId, final Integer vappId, final Integer vmId) { // XXX // TODO: Implement this modifier! return getVirtualMachine(vmId); } /** * Sets the virtual machine HD requirements based on the {@link VirtualMachineTemplate} * <p> * It also set the required CPU and RAM if it wasn't specified in the requested * {@link VirtualMachineDto}. * <p> * Specify the {@link EthernetDriverType} if the */ protected void setVirtualMachineTemplateRequirementsIfNotAlreadyDefined( final VirtualMachine vmachine, final VirtualMachineTemplate vmtemplate) { if (vmtemplate.getEthernetDriverType() != null) { vmachine.setEthernetDriverType(vmtemplate.getEthernetDriverType()); LOGGER.debug("VirtualMachine {} will use specific EthernetDriver {}", vmachine.getName(), vmtemplate.getEthernetDriverType().name()); } if (vmachine.getCpu() == 0) { vmachine.setCpu(vmtemplate.getCpuRequired()); } if (vmachine.getRam() == 0) { vmachine.setRam(vmtemplate.getRamRequired()); } if (vmtemplate.isStateful()) { vmachine.setHdInBytes(0); } else { vmachine.setHdInBytes(vmtemplate.getHdRequiredInBytes()); } } /** * This code is semiduplicated from VirtualMachineTemplateService but can't be used due cross * refrerence dep */ private VirtualMachineTemplate getVirtualMachineTemplateAndValidateEnterpriseAndDatacenter( final Integer enterpriseId, final Integer datacenterId, final Integer virtualMachineTemplateId) { Datacenter datacenter = infRep.findById(datacenterId); Enterprise enterprise = enterpriseRep.findById(enterpriseId); if (datacenter == null) { addNotFoundErrors(APIError.NON_EXISTENT_DATACENTER); } if (enterprise == null) { addNotFoundErrors(APIError.NON_EXISTENT_ENTERPRISE); } flushErrors(); DatacenterLimits limits = infRep.findDatacenterLimits(enterprise, datacenter); if (limits == null) { addConflictErrors(APIError.ENTERPRISE_NOT_ALLOWED_DATACENTER); flushErrors(); } VirtualMachineTemplate virtualMachineTemplate = appsLibRep.findVirtualMachineTemplateById(virtualMachineTemplateId); if (virtualMachineTemplate == null) { addNotFoundErrors(APIError.NON_EXISTENT_VIRTUAL_MACHINE_TEMPLATE); flushErrors(); } return virtualMachineTemplate; } /** * Creates the {@link NodeVirtualImage} that is the relation of {@link VirtualMachine} * {@link VirtualAppliance} and {@link VirtualMachineTemplate}. * * @param virtualMachine virtual machine to be associated with the virtual appliance. It must * contain the virtual machine template. * @param virtualAppliance void where the virtual machine exists. */ protected void createNodeVirtualImage(final VirtualMachine virtualMachine, final VirtualAppliance virtualAppliance, final String name) { LOGGER.debug("Create node virtual image with name virtual machine: {}", virtualMachine.getName()); NodeVirtualImage nodeVirtualImage = new NodeVirtualImage(name, virtualAppliance, virtualMachine.getVirtualMachineTemplate(), virtualMachine); repo.insertNodeVirtualImage(nodeVirtualImage); LOGGER.debug("Node virtual image created!"); } /** * Prepares the virtual image, in premium it sets the conversion. Attachs the conversion if * premium to the {@link VirtualMachine}. * * @param virtualDatacenter from where we retrieve the hypervisor type. * @param virtualMachine virtual machine to persist. * @return VirtualImage in premium the conversion. */ public void attachVirtualMachineTemplateConversion(final VirtualDatacenter virtualDatacenter, final VirtualMachine virtualMachine) { // COMMUNITY does nothing. LOGGER.debug("attachVirtualImageConversion community edition"); } /** * Deploys a {@link VirtualMachine}. This involves some steps. <br> * <ol> * <li>Select a machine to allocate the virtual machine</li> * <li>Check limits</li> * <li>Check resources</li> * <li>Check remote services</li> * <li>In premium call initiator</li> * <li>Subscribe to VSM</li> * <li>Build the Task DTO</li> * <li>Build the Configure DTO</li> * <li>Build the Power On DTO</li> * <li>Enqueue in tarantino</li> * <li>Register in redis</li> * <li>Add Task DTO to rabbitmq</li> * <li>Enable the resource <code>Progress<code></li> * </ol> * * @param vdcId VirtualDatacenter id * @param vappId VirtualAppliance id * @param vmId VirtualMachine id * @param forceEnterpriseSoftLimits Do we should take care of the soft limits? * @param restBuilder injected restbuilder context parameter * @throws Exception */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public String deployVirtualMachine(final Integer vmId, final Integer vappId, final Integer vdcId, final Boolean foreceEnterpriseSoftLimits) { VirtualAppliance vapp = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); VirtualMachine vmachine = getVirtualMachine(vdcId, vappId, vmId); allocate(vmachine, vapp, foreceEnterpriseSoftLimits); return sendDeploy(vmachine, vapp); } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void allocate(final VirtualMachine virtualMachine, final VirtualAppliance vapp, final Boolean foreceEnterpriseSoftLimits) { LOGGER.debug("Starting the deploy of the virtual machine {}", virtualMachine.getId()); // We need to operate with concrete and this also check that the VirtualMachine belongs to // those VirtualAppliance and VirtualDatacenter LOGGER.debug("Check for permissions"); // The user must have the proper permission userService.checkCurrentEnterpriseForPostMethods(virtualMachine.getEnterprise()); LOGGER.debug("Permission granted"); LOGGER.debug("Checking the virtual machine state. It must be in NOT_ALLOCATED"); // The remote services must be up for this Datacenter if we are to deploy // LOGGER.debug("Check remote services"); // FIXME checkRemoteServicesByVirtualDatacenter(vdcId); // LOGGER.debug("Remote services are ok!"); // Tasks needs the definition of the virtual machine // VirtualAppliance virtualAppliance = // getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); try { LOGGER.debug("Allocating with force enterpise soft limits : " + foreceEnterpriseSoftLimits); /* * Select a machine to allocate the virtual machine, Check limits, Check resources If * one of the above fail we cannot allocate the VirtualMachine */ vmAllocatorService.allocateVirtualMachine(virtualMachine, vapp, foreceEnterpriseSoftLimits); LOGGER.debug("Allocated!"); LOGGER.debug("Mapping the external volumes"); // We need to map all attached volumes if any initiatorMappings(virtualMachine); LOGGER.debug("Mapping done!"); } catch (APIException e) { traceApiExceptionVm(e, virtualMachine.getName()); /* * Select a machine to allocate the virtual machine, Check limits, Check resources If * one of the above fail we cannot allocate the VirtualMachine It also perform the * resource recompute */ if (virtualMachine.getHypervisor() != null) { vmAllocatorService.deallocateVirtualMachine(virtualMachine); } throw e; } catch (Exception ex) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, "virtualMachine.deploy", virtualMachine.getName()); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, ex, "virtualMachine.deploy", virtualMachine.getName()); if (virtualMachine.getHypervisor() != null) { vmAllocatorService.deallocateVirtualMachine(virtualMachine); } addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); } } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public String sendDeploy(final VirtualMachine virtualMachine, final VirtualAppliance virtualAppliance) { try { VirtualMachineDescriptionBuilder vmDesc = jobCreator.toTarantinoDto(virtualMachine, virtualAppliance); LOGGER.info("Generating the link to the status! {}", virtualMachine.getId()); return tarantino.deployVirtualMachine(virtualMachine, vmDesc); } catch (APIException e) { traceApiExceptionVm(e, virtualMachine.getName()); /* * Select a machine to allocate the virtual machine, Check limits, Check resources If * one of the above fail we cannot allocate the VirtualMachine It also perform the * resource recompute */ if (virtualMachine.getHypervisor() != null) { vmAllocatorService.deallocateVirtualMachine(virtualMachine); } throw e; } catch (Exception ex) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, "virtualMachine.deploy", virtualMachine.getName()); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, ex, "virtualMachine.deploy", virtualMachine.getName()); vmAllocatorService.deallocateVirtualMachine(virtualMachine); addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); return null; } } private void traceApiExceptionVm(final APIException exception, final String vmName) { if (exception.getErrors().isEmpty()) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, exception.getMessage(), vmName); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, exception, exception.getMessage(), vmName); } else { for (CommonError e : exception.getErrors()) { String msg = e.getCode() + " " + e.getMessage(); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, "virtualMachine.deploy.notEnoughResources", e.getCode(), vmName); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, exception, msg, vmName); } } } /** * Checks the {@link RemoteService} of the {@link VirtualDatacenter} and logs if any error. * * @param vdcId void */ private void checkRemoteServicesByVirtualDatacenter(final Integer vdcId) { LOGGER.debug("Checking remote services for virtual datacenter {}", vdcId); VirtualDatacenter virtualDatacenter = vdcService.getVirtualDatacenter(vdcId); ErrorsDto rsErrors = checkRemoteServiceStatusByDatacenter(virtualDatacenter.getDatacenter().getId()); if (!rsErrors.isEmpty()) { LOGGER.error("Some errors found while cheking remote services"); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors traceAdminErrors(rsErrors, SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, "remoteServices.down", true); // There is no point in continue addNotFoundErrors(APIError.GENERIC_OPERATION_ERROR); flushErrors(); } LOGGER.debug("Remote services Ok!"); } /** * Properly documented in Premium. * * @param virtualMachine void */ @Transactional(propagation = Propagation.REQUIRED, readOnly = false) public void initiatorMappings(final VirtualMachine virtualMachine) { // PREMIUM LOGGER.debug("initiatorMappings community edition"); } /** * /** Trace the Errors from a {@link ErrorsDto} to promote encapsulation. * * @param rsErrors void * @param severityType severity. * @param componentType component. * @param eventType type. * @param msg message. * @param appendExceptionMsg should we append the exception message? the format would be * <code>: error message</code> void */ private void traceAdminErrors(final ErrorsDto rsErrors, final SeverityType severityType, final ComponentType componentType, final EventType eventType, final String msg, final boolean appendExceptionMsg) { for (ErrorDto e : rsErrors.getCollection()) { tracer.systemLog(severityType, componentType, eventType, msg + (appendExceptionMsg ? ": " + e.getMessage() : "")); } } @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) public String undeployVirtualMachineHA(final Integer vmId, final Integer vappId, final Integer vdcId, final Boolean forceUndeploy, final VirtualMachineState originalState, final Hypervisor originalHypervisor) { LOGGER.debug("Starting the undeploy of the virtual machine {}", vmId); // We need to operate with concrete and this also check that the VirtualMachine belongs to // those VirtualAppliance and VirtualDatacenter VirtualMachine virtualMachine = getVirtualMachine(vdcId, vappId, vmId); if (!originalState.existsInHypervisor()) { return TaskResourceUtils.UNTRACEABLE_TASK; } LOGGER.debug("Check for permissions"); // The user must have the proper permission userService.checkCurrentEnterpriseForPostMethods(virtualMachine.getEnterprise()); LOGGER.debug("Permission granted"); LOGGER.debug("Check remote services"); // The remote services must be up for this Datacenter if we are to deploy checkRemoteServicesByVirtualDatacenter(vdcId); LOGGER.debug("Remote services are ok!"); VirtualAppliance virtualAppliance = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); if (!forceUndeploy && virtualMachine.isCaptured()) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_WILL_BE_DELETED); flushErrors(); } try { LOGGER.debug("Check remote services"); // The remote services must be up for this Datacenter if we are to deploy checkRemoteServicesByVirtualDatacenter(vdcId); LOGGER.debug("Remote services are ok!"); // Tasks needs the definition of the virtual machine VirtualMachineDescriptionBuilder vmDesc = jobCreator.toTarantinoDto(virtualMachine, virtualAppliance, true); String idAsyncTask = tarantino.undeployVirtualMachineHA(virtualMachine, vmDesc, originalState, originalHypervisor); LOGGER.info("Undeploying of the virtual machine id {} in the virtual factory!", virtualMachine.getId()); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "virtualMachine.enqueued", virtualMachine.getName()); // For the Admin to know all errors tracer .systemLog(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "virtualMachine.enqueuedTarantino", virtualMachine.getName()); return idAsyncTask; } catch (Exception e) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "virtualMachine.undeployError", e.toString(), virtualMachine.getName(), e.getMessage()); LOGGER .error( "Error undeploying setting the virtual machine to UNKNOWN virtual machine name {}: {}", virtualMachine.getUuid(), e.toString()); addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); } return null; } @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) public String undeployVirtualMachine(final Integer vmId, final Integer vappId, final Integer vdcId, final Boolean forceUndeploy, final VirtualMachineState originalState) { LOGGER.debug("Starting the undeploy of the virtual machine {}", vmId); // We need to operate with concrete and this also check that the VirtualMachine belongs to // those VirtualAppliance and VirtualDatacenter VirtualMachine virtualMachine = getVirtualMachine(vdcId, vappId, vmId); if (!originalState.existsInHypervisor()) { return TaskResourceUtils.UNTRACEABLE_TASK; } LOGGER.debug("Check for permissions"); // The user must have the proper permission userService.checkCurrentEnterpriseForPostMethods(virtualMachine.getEnterprise()); LOGGER.debug("Permission granted"); LOGGER.debug("Check remote services"); // The remote services must be up for this Datacenter if we are to deploy checkRemoteServicesByVirtualDatacenter(vdcId); LOGGER.debug("Remote services are ok!"); VirtualAppliance virtualAppliance = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); if (!forceUndeploy && virtualMachine.isCaptured()) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_WILL_BE_DELETED); flushErrors(); } try { // Tasks needs the definition of the virtual machine VirtualMachineDescriptionBuilder vmDesc = jobCreator.toTarantinoDto(virtualMachine, virtualAppliance); String idAsyncTask = tarantino.undeployVirtualMachine(virtualMachine, vmDesc, originalState); LOGGER.info("Undeploying of the virtual machine id {} in the virtual factory!", virtualMachine.getId()); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "virtualMachine.enqueued", virtualMachine.getName()); // For the Admin to know all errors tracer .systemLog(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "virtualMachine.enqueuedTarantino", virtualMachine.getName()); return idAsyncTask; } catch (Exception e) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "virtualMachine.undeployError", e.toString(), virtualMachine.getName(), e.getMessage()); LOGGER .error( "Error undeploying setting the virtual machine to UNKNOWN virtual machine name {}: {}", virtualMachine.getUuid(), e.toString()); addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); } return null; } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) private String undeployVirtualMachineAndDelete(final VirtualMachine virtualMachine, final Integer vappId, final Integer vdcId, final VirtualMachineState originalState) { LOGGER.debug("Check for permissions"); // The user must have the proper permission userService.checkCurrentEnterpriseForPostMethods(virtualMachine.getEnterprise()); LOGGER.debug("Permission granted"); LOGGER.debug("Check remote services"); // The remote services must be up for this Datacenter if we are to deploy checkRemoteServicesByVirtualDatacenter(vdcId); LOGGER.debug("Remote services are ok!"); VirtualAppliance virtualAppliance = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); try { // Tasks needs the definition of the virtual machine VirtualMachineDescriptionBuilder vmDesc = jobCreator.toTarantinoDto(virtualMachine, virtualAppliance); String idAsyncTask = tarantino.undeployVirtualMachineAndDelete(virtualMachine, vmDesc, originalState); LOGGER.info("Undeploying of the virtual machine id {} in the virtual factory!", virtualMachine.getId()); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "virtualMachine.enqueued", virtualMachine.getName()); // For the Admin to know all errors tracer .systemLog(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "virtualMachine.enqueuedTarantino", virtualMachine.getName()); return idAsyncTask; } catch (Exception e) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "virtualMachine.undeployError", e.toString(), virtualMachine.getName(), e.getMessage()); LOGGER .error( "Error undeploying setting the virtual machine to UNKNOWN virtual machine name {}: {}", virtualMachine.getUuid(), e.toString()); addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); } return null; } /** * Instance a {@link VirtualMachine}, handles all instance types {@link SnapshotType}. * * @param vmId {@link VirtualMachine} Id * @param vappId {@link VirtualAppliance} Id * @param vdcId {@link VirtualDatacenter} Id * @return The {@link Task} UUID */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public String instanceVirtualMachine(final Integer vmId, final Integer vappId, final Integer vdcId, final String instanceName, final VirtualMachineState originalState) { // Retrieve entities VirtualMachine virtualMachine = getVirtualMachine(vdcId, vappId, vmId); VirtualAppliance virtualApp = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); LOGGER.debug("Starting the instance of the virtual machine {}", virtualMachine.getName()); // Check if the operation is allowed and lock the virtual machine LOGGER.debug("Check for permissions"); userService.checkCurrentEnterpriseForPostMethods(virtualMachine.getEnterprise()); LOGGER.debug("Permission granted"); try { SnapshotType type = SnapshotType.getSnapshotType(virtualMachine); String taskId = null; LOGGER.debug("Instance type for virtual machine {} is {}", virtualMachine.getName(), type.name()); switch (type) { case FROM_ORIGINAL_DISK: case FROM_DISK_CONVERSION: taskId = tarantino.snapshotVirtualMachine(virtualApp, virtualMachine, originalState, instanceName); break; case FROM_IMPORTED_VIRTUALMACHINE: taskId = instanceImportedVirtualMachine(virtualApp, virtualMachine, originalState, instanceName); break; case FROM_STATEFUL_DISK: taskId = tarantino.instanceStatefulVirtualMachine(virtualApp, virtualMachine, originalState, instanceName); break; } if (taskId != null) { LOGGER.debug("Instance of virtual machine {} enqueued!", virtualMachine.getName()); } return taskId; } catch (APIException e) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_INSTANCE, "virtualMachine.instanceFailed", virtualMachine.getName()); tracer .systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_INSTANCE, e, "virtualMachine.instanceFailed", virtualMachine.getName()); throw e; } catch (Exception e) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_INSTANCE, "virtualMachine.instanceFailed", virtualMachine.getName()); tracer .systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_INSTANCE, e, "virtualMachine.instanceFailed", virtualMachine.getName()); addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); return null; } } /** * Performs an instance of type {@link SnapshotType#FROM_IMPORTED_VIRTUALMACHINE} * * @param virtualAppliance {@link VirtualAppliance} where the {@link VirtualMachine} is * contained. * @param virtualMachine The {@link VirtualMachine} to instance. * @param originalState The original {@link VirtualMachineState}. * @param instanceName The final name of the {@link VirtualMachineTemplate} * @return The {@link Task} UUID for progress tracking */ private String instanceImportedVirtualMachine(final VirtualAppliance virtualAppliance, final VirtualMachine virtualMachine, final VirtualMachineState originalState, final String instanceName) { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); // Create the folder structure in the destination repository String ovfPath = amService.preBundleTemplate(datacenter.getId(), virtualAppliance.getEnterprise() .getId(), instanceName); // Do the instance String snapshotPath = FilenameUtils.getFullPath(ovfPath); String snapshotFilename = FilenameUtils.getName(virtualMachine.getVirtualMachineTemplate().getPath()); return tarantino.snapshotVirtualMachine(virtualAppliance, virtualMachine, originalState, instanceName, snapshotPath, snapshotFilename); } /** * Changes the state of the VirtualMachine to the state passed * * @param vappId Virtual Appliance Id * @param vdcId VirtualDatacenter Id * @param state The state to which change * @throws Exception */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public String applyVirtualMachineState(final Integer vmId, final Integer vappId, final Integer vdcId, final VirtualMachineStateTransition stateTransition) { VirtualMachine virtualMachine = getVirtualMachine(vdcId, vappId, vmId); userService.checkCurrentEnterpriseForPostMethods(virtualMachine.getEnterprise()); try { VirtualAppliance virtualAppliance = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); VirtualMachineDescriptionBuilder machineDescriptionBuilder = jobCreator.toTarantinoDto(virtualMachine, virtualAppliance); String location = tarantino.applyVirtualMachineState(virtualMachine, machineDescriptionBuilder, stateTransition); LOGGER.info( "Applying the new state of the virtual machine id {} in the virtual factory!", virtualMachine.getId()); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_STATE, "virtualMachine.applyVirtualMachineEnqueued", virtualMachine.getName()); // For the Admin to know all errors tracer.systemLog(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_STATE, "virtualMachine.applyVirtualMachineTarantinoEnqueued", virtualMachine.getName()); // tasksService. // Here we add the url which contains the status return location; } catch (Exception ex) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, "virtualMachine.applyStateError", stateTransition.getEndState().name(), virtualMachine.getName()); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, ex, "virtualMachine.applyStateError", stateTransition .getEndState().name(), virtualMachine.getName()); addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); return null; } } /** * Reset the VirtualMachine to the state passed * * @param vappId Virtual Appliance Id * @param vdcId VirtualDatacenter Id * @param state The state to which change * @throws Exception */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public String resetVirtualMachine(final Integer vmId, final Integer vappId, final Integer vdcId, final VirtualMachineStateTransition state) { VirtualMachine virtualMachine = getVirtualMachine(vdcId, vappId, vmId); userService.checkCurrentEnterpriseForPostMethods(virtualMachine.getEnterprise()); VirtualAppliance virtualAppliance = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); try { VirtualMachineDescriptionBuilder machineDescriptionBuilder = jobCreator.toTarantinoDto(virtualMachine, virtualAppliance); String location = tarantino .applyVirtualMachineState(virtualMachine, machineDescriptionBuilder, state); LOGGER.info("Applying the reset of the virtual machine id {} in the virtual factory!", virtualMachine.getId()); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_STATE, "virtualMachine.resetVirtualMachineEnqueued", virtualMachine.getName()); // For the Admin to know all errors tracer.systemLog(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, EventType.VM_STATE, "virtualMachine.resetVirtualMachineTarantinoEnqueued"); // tasksService. // Here we add the url which contains the status return location; } catch (Exception ex) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, "virtualMachine.resetVirtualMachineError", virtualMachine.getName()); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, ex, "virtualMachine.resetVirtualMachineError", virtualMachine.getName()); addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); return null; } } /** * Checks one by one all {@link RemoteService} associated with the @{link Datacenter}. * * @param datacenterId * @return ErrorsDto */ public ErrorsDto checkRemoteServiceStatusByDatacenter(final Integer datacenterId) { List<RemoteService> remoteServicesByDatacenter = infRep.findRemoteServicesByDatacenter(infRep.findById(datacenterId)); ErrorsDto errors = new ErrorsDto(); for (RemoteService r : remoteServicesByDatacenter) { ErrorsDto checkRemoteServiceStatus = remoteServiceService.checkRemoteServiceStatus(r.getDatacenter(), r.getType(), r.getUri()); errors.addAll(checkRemoteServiceStatus); } return errors; } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public NodeVirtualImage getNodeVirtualImage(final Integer vdcId, final Integer vappId, final Integer vmId) { VirtualMachine vm = repo.findVirtualMachineById(vmId); VirtualAppliance vapp = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); VirtualDatacenter vdc = vdcRep.findById(vdcId); if (vm == null || !isAssignedTo(vmId, vapp.getId())) { LOGGER.error("Error retrieving the virtual machine: {} does not exist", vmId); addNotFoundErrors(APIError.NON_EXISTENT_VIRTUALMACHINE); flushErrors(); } LOGGER.debug("virtual machine {} found", vmId); NodeVirtualImage nodeVirtualImage = findNodeVirtualImage(vm); if (nodeVirtualImage == null) { LOGGER.error("Error retrieving the node virtual image of machine: {} does not exist", vmId); addNotFoundErrors(APIError.NODE_VIRTUAL_MACHINE_IMAGE_NOT_EXISTS); flushErrors(); } // if the ips are external, we need to set the limitID in order to return the // proper info. for (IpPoolManagement ip : vm.getIps()) { if (ip.getVlanNetwork().getEnterprise() != null) { // needed for REST links. DatacenterLimits dl = infRep.findDatacenterLimits(ip.getVlanNetwork().getEnterprise(), vdc.getDatacenter()); ip.getVlanNetwork().setLimitId(dl.getId()); } } return nodeVirtualImage; } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void deleteNodeVirtualImage(final NodeVirtualImage nvi) { repo.deleteNodeVirtualImage(nvi); } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public NodeVirtualImage findNodeVirtualImage(final VirtualMachine vm) { return repo.findNodeVirtualImageByVm(vm); } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<NodeVirtualImage> getNodeVirtualImages(final Integer vdcId, final Integer vappId) { VirtualAppliance vapp = getVirtualApplianceAndCheckVirtualDatacenter(vdcId, vappId); if (vapp == null) { LOGGER.error("Error retrieving the virtual appliance: {} does not exist", vappId); addNotFoundErrors(APIError.NON_EXISTENT_VIRTUALAPPLIANCE); flushErrors(); } LOGGER.debug("virtual appliance {} found", vappId); return vapp.getNodes(); } /** * Builds a {@link VirtualMachine} object from {@link VirtualMachineDto} object. * <p> * Allocated attributes (hypervisor/datastore) not set * * @param dto transfer input object * @return output pojo object. */ protected VirtualMachine buildVirtualMachineFromDto(final VirtualDatacenter vdc, final VirtualAppliance vapp, final VirtualMachineDto dto) { VirtualMachine vm = null; try { vm = ModelTransformer.persistenceFromTransport(VirtualMachine.class, dto); } catch (Exception e) { addUnexpectedErrors(APIError.STATUS_BAD_REQUEST); flushErrors(); } // get the machine template and set the values of the virtual machine // according with the template and its overriden values. vm.setVirtualMachineTemplate(getVirtualMachineTemplateFromDto(dto)); setVirtualMachineTemplateRequirementsIfNotAlreadyDefined(vm, vm.getVirtualMachineTemplate()); // Get the resources from dto List<IpPoolManagement> ips = getNICsFromDto(vdc, dto); List<DiskManagement> disks = getHardDisksFromDto(vdc, dto); // Set the values for the virtualmachine vm.setIps(ips); vm.setDisks(disks); vm.setPassword(dto.getPassword()); return vm; } /** * Allocate the POJOs of {@link DiskManagement} and {@link VolumeManagement} for the current * virtual datacenter, virtual appliance, virtual machine and the attachment order. * * @param vapp {@link VirtualAppliance} object where the resource will be allocated. * @param vm {@link VirtualMachine} object where the resource will be allocated. * @param resources the list of resources that will be allocated. */ protected void allocateNewStorages(final VirtualAppliance vapp, final VirtualMachine vm, final List< ? extends RasdManagement> resources, final List<Integer> blackList) { // When we allocate a resource, we need to set a unique attachment order for each one. // The function #getStorageFreeAttachmentSlot do the work. However, it only takes // the information from database, and we need to have a list of integers of the // already assigned slots before in the loop. 'blackList' stores them. for (RasdManagement resource : resources) { // Stateful volumes should not be trated as additional VM resources if (!isStatefulVolume(resource)) { boolean allocated = allocateResource(vm, vapp, resource, getFreeAttachmentSlot(blackList)); if (allocated) { // In Hyper-v only 2 attached volumes are allowed if (vm.getHypervisor() != null && vm.getHypervisor().getType() == HypervisorType.HYPERV_301 && blackList.size() >= 2) { addConflictErrors(APIError.VOLUME_TOO_MUCH_ATTACHMENTS); flushErrors(); } // if it is new allocated, we set the integer into the 'blacklisted' list. Integer blacklisted = resource.getSequence(); blackList.add(blacklisted); if (resource instanceof DiskManagement) { vdcRep.updateDisk((DiskManagement) resource); tracer.log(SeverityType.INFO, ComponentType.STORAGE_DEVICE, EventType.HARD_DISK_ASSIGN, "hardDisk.assigned", resource.getRasd() .getLimit(), vm.getName()); } else { storageRep.updateVolume((VolumeManagement) resource); tracer.log(SeverityType.INFO, ComponentType.VOLUME, EventType.VOLUME_ATTACH, "volume.attached", resource.getRasd() .getElementName(), resource.getRasd().getLimit(), vm.getName()); } } } } } private boolean isStatefulVolume(final RasdManagement resource) { return resource instanceof VolumeManagement && ((VolumeManagement) resource).isStateful(); } /** * Allocate the POJOs of {@link IpPoolManagement} for the current virtual datacenter, virtual * appliance, virtual machine and the attachment order. * * @param vapp {@link VirtualAppliance} object where the resource will be allocated. * @param vm {@link VirtualMachine} object where the resource will be allocated. * @param resources the list of resources that will be allocated. */ protected void allocateNewNICs(final VirtualAppliance vapp, final VirtualMachine vm, final List<IpPoolManagement> resources, final List<Integer> blackList) { // When we allocate a resource, we need to set a unique attachment order for each one. // The function #getStorageFreeAttachmentSlot do the work. However, it only takes // the information from database, and we need to have a list of integers of the // already assigned slots before in the loop. 'blackList' stores them. List<IpPoolManagement> ipPoolList = removeRepetedResources(resources); for (IpPoolManagement ip : resources) { boolean allocated = allocateResource(vm, vapp, ip, getFreeAttachmentSlot(blackList)); if (allocated) { if (ip.getVlanNetwork().getType().equals(NetworkType.EXTERNAL) || ip.getVlanNetwork().getType().equals(NetworkType.UNMANAGED)) { String mac = IPNetworkRang.requestRandomMacAddress(vapp.getVirtualDatacenter() .getHypervisorType()); String name = mac.replace(":", "") + "_host"; ip.setMac(mac); ip.setName(name); ip.setVirtualDatacenter(vapp.getVirtualDatacenter()); } Rasd rasd = NetworkService.createRasdEntity(vm, ip); vdcRep.insertRasd(rasd); ip.setRasd(rasd); vdcRep.updateIpManagement(ip); // if it is new allocated, we set the integer into the 'blacklisted' list. blackList.add(ip.getSequence()); tracer.log(SeverityType.INFO, ComponentType.NETWORK, EventType.NIC_ASSIGNED_VIRTUAL_MACHINE, "nic.attached", vm.getName(), ip.getIp(), ip.getVlanNetwork().getName()); } } } private List<IpPoolManagement> removeRepetedResources(final List<IpPoolManagement> resources) { Map<String, IpPoolManagement> ipMap = new HashMap<String, IpPoolManagement>(); int numberOfNewUnmanagedNICs = 0; for (IpPoolManagement ip : resources) { String mac = ip.getMac(); if (mac.equalsIgnoreCase("?")) { mac = mac + numberOfNewUnmanagedNICs; numberOfNewUnmanagedNICs++; } ipMap.put(mac, ip); } if (resources.size() > ipMap.size()) { String errorCode = APIError.RESOURCES_ALREADY_ASSIGNED.getCode(); String message = APIError.RESOURCES_ALREADY_ASSIGNED.getMessage(); CommonError error = new CommonError(errorCode, message); addNotFoundErrors(error); flushErrors(); } return new ArrayList<IpPoolManagement>(ipMap.values()); } /** * Dellocate the NICs of the {@link VirtualMachine} 'oldVm' parameter that are not anymore in * the new configuration 'newVm' parameter. Return the list of 'attachment orders' needed to * allocate * * @param oldVm {@link VirtualMachine} with the 'old' configuration. * @param newVm {@link VirtualMachine} with the 'new' configuration. * @return the list of attachment order still in oldVm */ protected List<Integer> dellocateOldNICs(final VirtualMachine oldVm, final VirtualMachine newVm) { List<Integer> oldNicsAttachments = new ArrayList<Integer>(); // dellocate the old ips that are not in the new virtual machine. for (IpPoolManagement ip : oldVm.getIps()) { if (!resourceIntoNewList(ip, newVm.getIps())) { // if the machine is NOT_ALLOCATED, the values here are definitive, // otherwise, it will be deleted in the handler if (oldVm.getState() == VirtualMachineState.NOT_ALLOCATED) { if (ip.getVlanNetwork().getType().equals(NetworkType.UNMANAGED)) { vdcRep.deleteRasd(ip.getRasd()); vdcRep.deleteIpPoolManagement(ip); } else if (ip.getVlanNetwork().getType().equals(NetworkType.EXTERNAL)) { ip.setMac(null); ip.setName(null); ip.setVirtualDatacenter(null); ip.detach(); vdcRep.deleteRasd(ip.getRasd()); vdcRep.updateIpManagement(ip); } else { ip.detach(); vdcRep.deleteRasd(ip.getRasd()); vdcRep.updateIpManagement(ip); } } else { ip.detach(); if (ip.getVlanNetwork().getType().equals(NetworkType.EXTERNAL)) { ip.setMac(null); ip.setName(null); ip.setVirtualDatacenter(null); } vdcRep.updateIpManagement(ip); tracer.log(SeverityType.INFO, ComponentType.NETWORK, EventType.NIC_ASSIGNED_VIRTUAL_MACHINE, "nic.released", oldVm.getName(), ip.getIp(), ip.getVlanNetwork().getName()); } // if the dellocated ip is the one with the default configuration, // and it is the last one, set the default configuration to null if (ip.itHasTheDefaultConfiguration(oldVm) && vdcRep.findIpsWithConfigurationIdInVirtualMachine(oldVm).size() == 0) { oldVm.setNetworkConfiguration(null); } } else { oldNicsAttachments.add(ip.getSequence()); } } return oldNicsAttachments; } /** * Dellocate the Disks of the {@link VirtualMachine} 'oldVm' parameter that are not anymore in * the new configuration 'newVm' parameter. Return the list of 'attachment orders' needed to * allocate after that. * * @param oldVm {@link VirtualMachine} with the 'old' configuration. * @param newVm {@link VirtualMachine} with the 'new' configuration. * @return the list of attachment order still in oldVm */ protected List<Integer> dellocateOldDisks(final VirtualMachine oldVm, final VirtualMachine newVm) { List<Integer> oldDisksAttachments = new ArrayList<Integer>(); // dellocate the old disks that are not in the new virtual machine. for (DiskManagement disk : oldVm.getDisks()) { if (!resourceIntoNewList(disk, newVm.getDisks())) { // if the machine is NOT_ALLOCATED, the values here are definitive, // otherwise, it will be deleted in the handler if (oldVm.getState() == VirtualMachineState.NOT_ALLOCATED) { vdcRep.deleteRasd(disk.getRasd()); rasdDao.remove(disk); tracer.log(SeverityType.INFO, ComponentType.STORAGE_DEVICE, EventType.HARD_DISK_ASSIGN, "hardDisk.released", disk.getSizeInMb(), oldVm.getName()); } else { disk.detach(); vdcRep.updateDisk(disk); } } else { oldDisksAttachments.add(disk.getSequence()); } } return oldDisksAttachments; } /** * Dellocate the Volumes of the {@link VirtualMachine} 'oldVm' parameter that are not anymore in * the new configuration 'newVm' parameter. Return the list of 'attachment orders' needed to * allocate after that. * * @param oldVm {@link VirtualMachine} with the 'old' configuration. * @param newVm {@link VirtualMachine} with the 'new' configuration. * @return the list of attachment order still in oldVm */ protected List<Integer> dellocateOldVolumes(final VirtualMachine oldVm, final VirtualMachine newVm) { List<Integer> oldVolumesAttachments = new ArrayList<Integer>(); // dellocate the old disks that are not in the new virtual machine. for (VolumeManagement vol : oldVm.getVolumes()) { // Stateful volumes should not be trated as additional VM resources if (!vol.isStateful()) { if (!resourceIntoNewList(vol, newVm.getVolumes())) { if (!vol.isAttached()) { addConflictErrors(APIError.VOLUME_NOT_ATTACHED); flushErrors(); } vol.detach(); storageRep.updateVolume(vol); tracer.log(SeverityType.INFO, ComponentType.VOLUME, EventType.VOLUME_DETACH, "volume.detached", vol.getName(), vol.getSizeInMB(), oldVm.getName()); } else { oldVolumesAttachments.add(vol.getSequence()); } } } return oldVolumesAttachments; } /** * Get the next free attachment slot to be used to attach a disk, volume, or nic to the virtual * machine. * * @param vm The virtual machine where the disk will be attached. * @return The free slot to use. */ protected int getFreeAttachmentSlot(final List<Integer> blackList) { // Find the first free slot int i = RasdManagement.FIRST_ATTACHMENT_SEQUENCE; while (true) { if (!blackList.contains(i)) { return i; // Found gap } i++; } } /** * Validates the given object with links to a hard disk and returns the referenced hard disk. * * @param links The links to validate the hard disk. * @param expectedVirtualDatacenter The expected virtual datacenter to be found in the link. * @return The list of {@link DiskManagement} referenced by the link. * @throws Exception If the link is not valid. */ public List<DiskManagement> getHardDisksFromDto(final VirtualDatacenter vdc, final SingleResourceTransportDto dto) { List<DiskManagement> disks = new LinkedList<DiskManagement>(); // Validate and load each volume from the link list for (RESTLink link : dto.searchLinks(DiskResource.DISK)) { String path = buildPath(VirtualDatacentersResource.VIRTUAL_DATACENTERS_PATH, VirtualDatacenterResource.VIRTUAL_DATACENTER_PARAM, DisksResource.DISKS_PATH, DiskResource.DISK_PARAM); MultivaluedMap<String, String> pathValues = URIResolver.resolveFromURI(path, link.getHref()); // URI needs to have an identifier to a VDC, and another one to the volume if (pathValues == null || !pathValues.containsKey(VirtualDatacenterResource.VIRTUAL_DATACENTER) || !pathValues.containsKey(DiskResource.DISK)) { throw new BadRequestException(APIError.HD_ATTACH_INVALID_LINK); } // Volume provided in link must belong to the same virtual datacenter Integer vdcId = Integer.parseInt(pathValues.getFirst(VirtualDatacenterResource.VIRTUAL_DATACENTER)); if (!vdcId.equals(vdc.getId())) { throw new BadRequestException(APIError.HD_ATTACH_INVALID_VDC_LINK); } Integer diskId = Integer.parseInt(pathValues.getFirst(DiskResource.DISK)); DiskManagement disk = vdcRep.findHardDiskByVirtualDatacenter(vdc, diskId); if (disk == null) { String errorCode = APIError.HD_NON_EXISTENT_HARD_DISK.getCode(); String message = APIError.HD_NON_EXISTENT_HARD_DISK.getMessage() + ": Hard Disk id " + diskId; CommonError error = new CommonError(errorCode, message); addNotFoundErrors(error); } else { disks.add(disk); } } // Throw the exception with all the disks we have not found. flushErrors(); return disks; } /** * Validates the given object with links to a NIC and returns the referenced list of * {@link IpPoolManagement}. * * @param links The links to validate the hard disk. * @param expectedVirtualDatacenter The expected virtual datacenter to be found in the link. * @return The list of {@link IpPoolManagement} referenced by the link. * @throws Exception If the link is not valid. */ public List<IpPoolManagement> getNICsFromDto(final VirtualDatacenter vdc, final SingleResourceTransportDto dto) { List<IpPoolManagement> ips = new LinkedList<IpPoolManagement>(); // Validate and load each volume from the link list for (RESTLink link : dto.searchLinks(PrivateNetworkResource.PRIVATE_IP)) { // Parse the URI with the expected parameters and extract the identifier values. String buildPath = buildPath(VirtualDatacentersResource.VIRTUAL_DATACENTERS_PATH, VirtualDatacenterResource.VIRTUAL_DATACENTER_PARAM, PrivateNetworksResource.PRIVATE_NETWORKS_PATH, PrivateNetworkResource.PRIVATE_NETWORK_PARAM, IpAddressesResource.IP_ADDRESSES, IpAddressesResource.IP_ADDRESS_PARAM); MultivaluedMap<String, String> ipsValues = URIResolver.resolveFromURI(buildPath, link.getHref()); // URI needs to have an identifier to a VDC, another one to a Private Network // and another one to Private IP if (ipsValues == null || !ipsValues.containsKey(VirtualDatacenterResource.VIRTUAL_DATACENTER) || !ipsValues.containsKey(PrivateNetworkResource.PRIVATE_NETWORK) || !ipsValues.containsKey(IpAddressesResource.IP_ADDRESS)) { throw new BadRequestException(APIError.VLANS_PRIVATE_IP_INVALID_LINK); } // Private IP must belong to the same Virtual Datacenter where the Virtual Machine // belongs to. Integer idVdc = Integer.parseInt(ipsValues.getFirst(VirtualDatacenterResource.VIRTUAL_DATACENTER)); if (!idVdc.equals(vdc.getId())) { throw new BadRequestException(APIError.VLANS_IP_LINK_INVALID_VDC); } // Extract the vlanId and ipId values to execute the association. Integer vlanId = Integer.parseInt(ipsValues.getFirst(PrivateNetworkResource.PRIVATE_NETWORK)); Integer ipId = Integer.parseInt(ipsValues.getFirst(IpAddressesResource.IP_ADDRESS)); VLANNetwork vlan = vdcRep.findVlanByVirtualDatacenterId(vdc, vlanId); if (vlan == null) { String errorCode = APIError.VLANS_NON_EXISTENT_VIRTUAL_NETWORK.getCode(); String message = APIError.VLANS_NON_EXISTENT_VIRTUAL_NETWORK.getMessage() + ": Vlan id " + vlanId; CommonError error = new CommonError(errorCode, message); addNotFoundErrors(error); continue; } IpPoolManagement ip = vdcRep.findIp(vlan, ipId); if (ip == null) { String errorCode = APIError.NON_EXISTENT_IP.getCode(); String message = APIError.NON_EXISTENT_IP.getMessage() + ": Vlan id " + vlan.getId(); CommonError error = new CommonError(errorCode, message); addNotFoundErrors(error); continue; } ips.add(ip); } // Throw the exception with all the disks we have not found. flushErrors(); return ips; } /** * Extracts the proper network configuration from the "network_configuration" link. * * @param vapp VirtualAppliance We need it to check it the link os correct. * @param newvm VirtualMachine new. We need to check if the configuration is correct for the * current virtualmachine's NICs. * @param configurationRef reference to configuration. * @return the {@link NetworkConfiguration} object to set to VirtualMachine. */ public NetworkConfiguration getNetworkConfigurationFromDto(final VirtualAppliance vapp, final VirtualMachine newvm, final SingleResourceTransportDto dto) { RESTLink link = dto.searchLink(VirtualMachineNetworkConfigurationResource.DEFAULT_CONFIGURATION); if (link == null) { // we disable the default network configuration. return null; } String networkConfigurationTemplate = buildPath(VirtualDatacentersResource.VIRTUAL_DATACENTERS_PATH, VirtualDatacenterResource.VIRTUAL_DATACENTER_PARAM, VirtualAppliancesResource.VIRTUAL_APPLIANCES_PATH, VirtualApplianceResource.VIRTUAL_APPLIANCE_PARAM, VirtualMachinesResource.VIRTUAL_MACHINES_PATH, VirtualMachineResource.VIRTUAL_MACHINE_PARAM, VirtualMachineNetworkConfigurationResource.NETWORK, VirtualMachineNetworkConfigurationResource.CONFIGURATION_PATH, VirtualMachineNetworkConfigurationResource.CONFIGURATION_PARAM); MultivaluedMap<String, String> configurationValues = URIResolver.resolveFromURI(networkConfigurationTemplate, link.getHref()); // URI needs to have an identifier to a VDC, another one to a Private Network // and another one to Private IP if (configurationValues == null || !configurationValues.containsKey(VirtualDatacenterResource.VIRTUAL_DATACENTER) || !configurationValues.containsKey(VirtualApplianceResource.VIRTUAL_APPLIANCE) || !configurationValues.containsKey(VirtualMachineResource.VIRTUAL_MACHINE) || !configurationValues .containsKey(VirtualMachineNetworkConfigurationResource.CONFIGURATION)) { throw new BadRequestException(APIError.NETWORK_INVALID_CONFIGURATION_LINK); } // Get the identifiers of the link Integer vdcId = Integer.parseInt(configurationValues .getFirst(VirtualDatacenterResource.VIRTUAL_DATACENTER)); Integer vappId = Integer.parseInt(configurationValues .getFirst(VirtualApplianceResource.VIRTUAL_APPLIANCE)); Integer vmId = Integer.parseInt(configurationValues.getFirst(VirtualMachineResource.VIRTUAL_MACHINE)); Integer configId = Integer.parseInt(configurationValues .getFirst(VirtualMachineNetworkConfigurationResource.CONFIGURATION)); // Check the identifiers if (!vdcId.equals(vapp.getVirtualDatacenter().getId())) { throw new BadRequestException(APIError.NETWORK_LINK_INVALID_VDC); } if (!vappId.equals(vapp.getId())) { throw new BadRequestException(APIError.NETWORK_LINK_INVALID_VAPP); } if (!vmId.equals(newvm.getTemporal())) // it is the new resource, the id it is in the // 'temporal' { throw new BadRequestException(APIError.NETWORK_LINK_INVALID_VM); } List<IpPoolManagement> ips = newvm.getIps(); for (IpPoolManagement ip : ips) { if (ip.getVlanNetwork().getConfiguration().getId().equals(configId)) { return ip.getVlanNetwork().getConfiguration(); } } // if we have reached this point, it means the configuratin id is not valid throw new BadRequestException(APIError.NETWORK_LINK_INVALID_CONFIG); } /** * Get the object {@link VirtualMachineTemplate} from the input dto. * * @param dto the object that should have the link to a virtual machine template. * @return the found {@link virtualMachineTemplateObject} */ public VirtualMachineTemplate getVirtualMachineTemplateFromDto( final SingleResourceTransportDto dto) { String vmTemplatePath = buildPath( EnterprisesResource.ENTERPRISES_PATH, EnterpriseResource.ENTERPRISE_PARAM, // DatacenterRepositoriesResource.DATACENTER_REPOSITORIES_PATH, DatacenterRepositoryResource.DATACENTER_REPOSITORY_PARAM, VirtualMachineTemplatesResource.VIRTUAL_MACHINE_TEMPLATES_PATH, VirtualMachineTemplateResource.VIRTUAL_MACHINE_TEMPLATE_PARAM); RESTLink link = dto.searchLink(VIRTUAL_MACHINE_TEMPLATE); if (link == null) { addValidationErrors(APIError.LINKS_VIRTUAL_MACHINE_TEMPLATE_NOT_FOUND); flushErrors(); } Integer entId = null; Integer dcId = null; Integer templId = null; try { MultivaluedMap<String, String> pathValues = URIResolver.resolveFromURI(vmTemplatePath, link.getHref()); // URI needs to have an identifier to a ENTERPRISE, another to a DATACENTER_REPOSITORY // and another one to the TEMPLATE if (pathValues == null || !pathValues.containsKey(EnterpriseResource.ENTERPRISE) || !pathValues.containsKey(DatacenterRepositoryResource.DATACENTER_REPOSITORY) || !pathValues.containsKey(VirtualMachineTemplateResource.VIRTUAL_MACHINE_TEMPLATE)) { throw new BadRequestException(APIError.LINKS_VIRTUAL_MACHINE_TEMPLATE_INVALID_URI); } entId = Integer.valueOf(pathValues.getFirst(EnterpriseResource.ENTERPRISE)); dcId = Integer.valueOf(pathValues .getFirst(DatacenterRepositoryResource.DATACENTER_REPOSITORY)); templId = Integer.valueOf(pathValues .getFirst(VirtualMachineTemplateResource.VIRTUAL_MACHINE_TEMPLATE)); } catch (Exception e) { // unhandled exception parsing the uri addValidationErrors(APIError.LINKS_INVALID_LINK); flushErrors(); } return getVirtualMachineTemplateAndValidateEnterpriseAndDatacenter(entId, dcId, templId); } /** * Gets a VirtualDatacenter. Raises an exception if it does not exist. * * @param vdcId identifier of the virtual datacenter. * @return the found {@link VirtualDatacenter} instance. */ protected VirtualDatacenter getVirtualDatacenter(final Integer vdcId) { VirtualDatacenter vdc = vdcRep.findById(vdcId); if (vdc == null) { addNotFoundErrors(APIError.NON_EXISTENT_VIRTUAL_DATACENTER); flushErrors(); } return vdc; } /** * Creates the backup object with a virtual machine. * * @param vm original {@link VirtualMachine} obj. * @return the backup object. */ public VirtualMachine duplicateVirtualMachineObject(final VirtualMachine vm) { VirtualMachine backUpVm = createBackUpMachine(vm); // set the 'real' name backUpVm.setName(vm.getName()); createBackUpResources(vm, backUpVm); return backUpVm; } /** * Copy of the Virtual Machine object. * * @param vm {@link VirtualMachine} object original * @return the copy of the input param. */ protected VirtualMachine createBackUpMachine(final VirtualMachine vm) { VirtualMachine tmp = new VirtualMachine(); // backup virtual machine properties tmp.setCpu(vm.getCpu()); tmp.setDatastore(vm.getDatastore()); tmp.setDescription(vm.getDescription()); tmp.setEnterprise(vm.getEnterprise()); tmp.setHdInBytes(vm.getHdInBytes()); tmp.setHighDisponibility(vm.getHighDisponibility()); tmp.setHypervisor(vm.getHypervisor()); tmp.setIdType(vm.getIdType()); tmp.setName("tmp_" + vm.getName()); tmp.setPassword(vm.getPassword()); tmp.setRam(vm.getRam()); tmp.setState(VirtualMachineState.LOCKED); tmp.setSubState(vm.getSubState()); tmp.setUser(vm.getUser()); tmp.setUuid(vm.getUuid()); tmp.setVdrpIP(vm.getVdrpIP()); tmp.setVdrpPort(vm.getVdrpPort()); tmp.setVirtualImageConversion(vm.getVirtualImageConversion()); tmp.setVirtualMachineTemplate(vm.getVirtualMachineTemplate()); tmp.setNetworkConfiguration(vm.getNetworkConfiguration()); tmp.setTemporal(vm.getId()); return tmp; } /** * Copy the resources of the Virtual Machine. * * @param vm original virtualmachine * @param tmp backup virtualmachine where the resources will be copied. */ protected void createBackUpResources(final VirtualMachine vm, final VirtualMachine tmp) { // Backup disks List<DiskManagement> disksTemp = new ArrayList<DiskManagement>(); for (DiskManagement disk : vm.getDisks()) { DiskManagement disktmp = new DiskManagement(); disktmp.setSequence(disk.getSequence()); disktmp.setDatastore(disk.getDatastore()); disktmp.setTemporal(disk.getId()); disktmp.setIdResourceType(disk.getIdResourceType()); disktmp.setRasd(disk.getRasd()); disktmp.setReadOnly(disk.getReadOnly()); disktmp.setSizeInMb(disk.getSizeInMb()); disktmp.setVirtualAppliance(disk.getVirtualAppliance()); disktmp.setVirtualDatacenter(disk.getVirtualDatacenter()); disktmp.setVirtualMachine(tmp); disksTemp.add(disktmp); } tmp.setDisks(disksTemp); // Backup NICs List<IpPoolManagement> ipsTemp = new ArrayList<IpPoolManagement>(); for (IpPoolManagement ip : vm.getIps()) { IpPoolManagement ipTmp = new IpPoolManagement(); ipTmp.setSequence(ip.getSequence()); ipTmp.setTemporal(ip.getId()); ipTmp.setIdResourceType(ip.getIdResourceType()); ipTmp.setRasd(ip.getRasd()); ipTmp.setVirtualAppliance(ip.getVirtualAppliance()); ipTmp.setVirtualDatacenter(ip.getVirtualDatacenter()); ipTmp.setVirtualMachine(tmp); ipTmp.setName(ip.getName()); ipTmp.setVlanNetwork(ip.getVlanNetwork()); ipTmp.setMac(ip.getMac()); ipTmp.setAvailable(ip.getAvailable()); ipTmp.setNetworkName(ip.getNetworkName()); ipTmp.setQuarantine(ip.getQuarantine()); ipTmp.setIp(ip.getIp()); ipsTemp.add(ipTmp); } tmp.setIps(ipsTemp); // Backup Volumes List<VolumeManagement> volsTemp = new ArrayList<VolumeManagement>(); for (VolumeManagement vol : vm.getVolumes()) { // Stateful volumes should not be trated as VM attached resources if (!vol.isStateful()) { VolumeManagement volTmp = new VolumeManagement(); volTmp.setSequence(vol.getSequence()); volTmp.setTemporal(vol.getId()); volTmp.setIdResourceType(vol.getIdResourceType()); volTmp.setRasd(vol.getRasd()); volTmp.setVirtualAppliance(vol.getVirtualAppliance()); volTmp.setVirtualDatacenter(vol.getVirtualDatacenter()); volTmp.setVirtualMachine(tmp); volTmp.setStoragePool(vol.getStoragePool()); volTmp.setVirtualMachineTemplate(vol.getVirtualMachineTemplate()); volTmp.setIdScsi(vol.getIdScsi()); volTmp.setState(vol.getState()); volTmp.setUsedSizeInMB(vol.getUsedSizeInMB()); volsTemp.add(volTmp); } } tmp.setVolumes(volsTemp); } /** * Gets the {@link VirtualMachine} backup created to store reconfigure previous state. * * @return the virtualmachine with ''temp'' == provided vm identifier */ public VirtualMachine getBackupVirtualMachine(final VirtualMachine vmachine) { final VirtualMachine vmbackup = repo.findBackup(vmachine); if (vmbackup == null) { addNotFoundErrors(APIError.VIRTUAL_MACHINE_BACKUP_NOT_FOUND); flushErrors(); } return vmbackup; } /** * Cleanup backup resources */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void deleteBackupResources(final VirtualMachine backUpVm) { try { rasdDao.enableTemporalOnlyFilter(); List<RasdManagement> rasds = backUpVm.getRasdManagements(); // First of all, we have to release the VLAN tags if it is needed. // This is not a very optimal algorithm, since we traverse again the Rasds later, // but we need to know the id // of the virtual machine before to delete the entity to know if we can to release the // TAG. for (RasdManagement rollbackRasd : rasds) { if (rollbackRasd instanceof IpPoolManagement) { IpPoolManagement originalRasd = (IpPoolManagement) rasdDao.findById(rollbackRasd.getTemporal()); if (!originalRasd.isAttached()) { // check if it was the last IP of the VLAN, and release the VLAN // tag. VLANNetwork vlanNetwork = originalRasd.getVlanNetwork(); final boolean assigned = ipPoolManDao.isVlanAssignedToDifferentVM(backUpVm.getId(), vlanNetwork); if (!assigned) { if (vlanNetwork.getType().equals(NetworkType.INTERNAL)) { vlanNetwork.setTag(null); } NetworkAssignment na = netAssignDao.findByVlanNetwork(vlanNetwork); if (na != null) { netAssignDao.remove(na); } } } } } /* * CAUTION! We need this flush exactly here. Otherwise it tries to set teh * vlanNetwork.setTag(null) after to delete the related IpPoolManagement (that will be * deleted in the next loop) and it raises an UnknowEntityException. */ vlanNetworkDao.flush(); // we need to first delete the vm (as it updates the rasd_man) repo.deleteVirtualMachine(backUpVm); for (RasdManagement rollbackRasd : rasds) { if (rollbackRasd instanceof IpPoolManagement) { IpPoolManagement originalRasd = (IpPoolManagement) rasdDao.findById(rollbackRasd.getTemporal()); if (!originalRasd.isAttached()) { // remove the rasd vdcRep.deleteRasd(originalRasd.getRasd()); // unmanaged ips disappear when the are not assigned to a virtual machine. if (originalRasd.isUnmanagedIp()) { rasdDao.remove(originalRasd); } // external ips should remove its MAC and name if (originalRasd.isExternalIp()) { originalRasd.setMac(null); originalRasd.setName(null); } } } // DiskManagements always are deleted if (rollbackRasd instanceof DiskManagement) { DiskManagement originalRasd = (DiskManagement) rasdDao.findById(rollbackRasd.getTemporal()); if (!originalRasd.isAttached()) { vdcRep.deleteRasd(originalRasd.getRasd()); rasdDao.remove(originalRasd); } } // refresh as the vm delete was updated the rasd rasdDao.remove(rasdDao.findById(rollbackRasd.getId())); } rasdDao.flush(); } finally { rasdDao.restoreDefaultFilters(); } // FIXME This is what we like // try // { // rasdDao.enableTemporalOnlyFilter(); // // for (RasdManagement rasd : vm.getRasdManagements()) // { // rasdDao.remove(rasd); // } // // repo.deleteVirtualMachine(repo.findVirtualMachineById(vm.getId())); // // rasdDao.flush(); // } // finally // { // rasdDao.restoreDefaultFilters(); // } } /** * Updates all the attributes and resource attachments of ''updatedVm'' from the backup * ''rollbackVm''. * * @param updatedVm, current state of the virtual machine (not applied in the hypervisor) * @param rollbackVm, state of updaedVm previous to the reconfigure. * @return updatedVm with the attributes and resource attachments of rollbackVm. */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public VirtualMachine restoreBackupVirtualMachine(final VirtualMachine updatedVm, final VirtualMachine rollbackVm) { // will use VsmServiceStub to force a refresh // updatedVm.setState(VirtualMachineState.LOCKED); // backup virtual machine properties updatedVm.setCpu(rollbackVm.getCpu()); updatedVm.setDatastore(rollbackVm.getDatastore()); updatedVm.setDescription(rollbackVm.getDescription()); updatedVm.setEnterprise(rollbackVm.getEnterprise()); updatedVm.setHdInBytes(rollbackVm.getHdInBytes()); updatedVm.setHighDisponibility(rollbackVm.getHighDisponibility()); updatedVm.setHypervisor(rollbackVm.getHypervisor()); updatedVm.setIdType(rollbackVm.getIdType()); updatedVm.setName(rollbackVm.getName().substring("tmp_".length())); updatedVm.setPassword(rollbackVm.getPassword()); updatedVm.setRam(rollbackVm.getRam()); updatedVm.setSubState(rollbackVm.getSubState()); updatedVm.setUser(rollbackVm.getUser()); updatedVm.setUuid(rollbackVm.getUuid()); updatedVm.setVdrpIP(rollbackVm.getVdrpIP()); updatedVm.setVdrpPort(rollbackVm.getVdrpPort()); updatedVm.setVirtualImageConversion(rollbackVm.getVirtualImageConversion()); updatedVm.setVirtualMachineTemplate(rollbackVm.getVirtualMachineTemplate()); updatedVm.setNetworkConfiguration(rollbackVm.getNetworkConfiguration()); List<RasdManagement> updatedResources = updatedVm.getRasdManagements(); List<RasdManagement> rollbackResources = getBackupResources(rollbackVm); LOGGER.debug("removed backup virtual machine"); for (RasdManagement updatedRasd : updatedResources) { RasdManagement rollbackRasd = getBackupResource(rollbackResources, updatedRasd.getId()); if (rollbackRasd == null) { LOGGER.trace("restore: detach resource " + updatedRasd.getId()); if (updatedRasd instanceof IpPoolManagement) { IpPoolManagement originalRasd = (IpPoolManagement) updatedRasd; // remove the rasd vdcRep.deleteRasd(originalRasd.getRasd()); // unmanaged ips disappear when the are not assigned to a virtual machine. if (originalRasd.isUnmanagedIp()) { rasdDao.remove(originalRasd); } } // DiskManagements always are deleted if (updatedRasd instanceof DiskManagement) { DiskManagement originalRasd = (DiskManagement) updatedRasd; vdcRep.deleteRasd(originalRasd.getRasd()); rasdDao.remove(originalRasd); } else { // volumes only need to be dettached if (!isStatefulVolume(updatedRasd)) { updatedRasd.detach(); } } } } for (RasdManagement rollbackRasd : rollbackResources) { RasdManagement originalRasd = rasdDao.findById(rollbackRasd.getTemporal()); if (!originalRasd.isAttached()) { // Re attach the resource to the virtual machine LOGGER.trace("restore: attach resource " + originalRasd.getId()); originalRasd.attach(originalRasd.getSequence(), updatedVm); // I dunno if it is necessary for the rest of resources, // but for IPs it is. if (originalRasd instanceof IpPoolManagement) { IpPoolManagement ipman = (IpPoolManagement) originalRasd; IpPoolManagement ipRoll = (IpPoolManagement) rollbackRasd; VirtualAppliance vapp = vdcRep.findVirtualApplianceByVirtualMachine(updatedVm); ipman.setVirtualAppliance(vapp); ipman.setVirtualDatacenter(rollbackRasd.getVirtualDatacenter()); ipman.setIp(ipRoll.getIp()); ipman.setMac(ipRoll.getMac()); } } } repo.deleteVirtualMachine(rollbackVm); repo.update(updatedVm); rasdDao.flush(); // update virtual machine resources LOGGER.info("restored virtual machine {} from backup", updatedVm.getUuid()); return updatedVm; } /** * Get the resources attached to the provided backup virtualmachine. */ private List<RasdManagement> getBackupResources(final VirtualMachine rollbackVm) { try { rasdDao.enableTemporalOnlyFilter(); return rollbackVm.getRasdManagements(); } finally { rasdDao.restoreDefaultFilters(); } } /** * Find the backup resources with temporal pointing to the provided resource identifier. * * @return resource with temporal == provided resource id, null if not found */ private RasdManagement getBackupResource(final List<RasdManagement> rollbackResources, final Integer tempRasdManId) { for (RasdManagement rasdman : rollbackResources) { if (tempRasdManId.equals(rasdman.getTemporal())) { return rasdman; } } return null; } /** * @param vmId to return * @return VirtualMachine with DC. */ public VirtualMachine getVirtualMachineInitialized(final Integer vmId) { VirtualMachine virtualMachine = repo.findVirtualMachineById(vmId); if (virtualMachine == null) { return null; } if (virtualMachine.getHypervisor() != null) { Hibernate.initialize(virtualMachine.getHypervisor().getMachine().getDatacenter()); } if (virtualMachine.getEnterprise() != null) { Hibernate.initialize(virtualMachine.getEnterprise()); } if (virtualMachine.getDatastore() != null) { Hibernate.initialize(virtualMachine.getDatastore()); } if (virtualMachine.getVirtualMachineTemplate() != null) { Hibernate.initialize(virtualMachine.getVirtualMachineTemplate()); } return virtualMachine; } /** * Sets the {@link VirtualMachine#setState(VirtualMachineState)} to * {@link VirtualMachineState#UNKNOWN}. */ @Transactional(propagation = Propagation.REQUIRES_NEW) public void setVirtualMachineToUnknown(final Integer vmId) { repo.setVirtualMachineToUnknown(vmId); } /** * Provides a standard method to allocate a resource and check if its already allocated. * * @param vm {@link VirtualMachine} virtual machine where the resource will be allocated. * @param vapp {@link VirtualAppliance} virtual appiance where the resource will be allocated. * @param resource resource to allocate * @param attachOrder the number of allocation order for this resource. * @return true if the resource has been allocated, false if it was previously allocated. */ protected boolean allocateResource(final VirtualMachine vm, final VirtualAppliance vapp, final RasdManagement resource, final Integer attachOrder) { if (resource.isAttached()) { // FIXME BE AWARE OF IT: // the provided vm sometimes have ID (came form DDBB) and sometimes havent ID // (createBackup) but have the TemporalID. So it is not always called with the same type // of parameter. final Integer currentId = resource.getVirtualMachine().getId() != null ? resource.getVirtualMachine().getId() : resource.getVirtualMachine().getTemporal(); if (!currentId.equals(vm.getId())) { addConflictErrors(APIError.RESOURCE_ALREADY_ASSIGNED_TO_A_VIRTUAL_MACHINE); flushErrors(); } return false; } if (resource.getVirtualMachine() != null && resource.getVirtualMachine().getTemporal() != null) { if (!resource.getVirtualMachine().getTemporal().equals(vm.getId())) { addConflictErrors(APIError.RESOURCE_ALREADY_ASSIGNED_TO_A_VIRTUAL_MACHINE); flushErrors(); } // else do nothing, the resource is already asigned to this virtual machine. return false; } else { resource.attach(attachOrder, vm, vapp); return true; } } /** * This method writes without care for permissions. * * @param vm void */ @Transactional(propagation = Propagation.REQUIRED) public void updateVirtualMachineBySystem(final VirtualMachine vm) { repo.update(vm); } /** * This method writes without care for permissions. * * @param vm void */ @Transactional(propagation = Propagation.REQUIRED) public void insertNodeVirtualImage(final NodeVirtualImage node) { repo.insertNodeVirtualImage(node); } /** * This method writes without care for permissions. * * @param vm void */ @Transactional(propagation = Propagation.REQUIRED) public void insertVirtualMachine(final VirtualMachine virtualMachine) { repo.insert(virtualMachine); } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Collection<VirtualMachine> getManagedByHypervisor(final Hypervisor hypervisor) { return repo.findManagedByHypervisor(hypervisor); } }