/** * 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 java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.EntityManager; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.abiquo.api.exceptions.APIError; import com.abiquo.api.exceptions.APIException; import com.abiquo.api.services.DatacenterService; import com.abiquo.api.services.DefaultApiService; import com.abiquo.api.services.EnterpriseService; import com.abiquo.api.services.TaskService; import com.abiquo.api.services.UserService; import com.abiquo.api.services.VirtualMachineAllocatorService; import com.abiquo.api.services.stub.TarantinoService; import com.abiquo.api.spring.security.SecurityService; import com.abiquo.model.transport.error.CommonError; import com.abiquo.scheduler.VirtualMachineRequirementsFactory; import com.abiquo.scheduler.limit.VirtualMachinePrice; import com.abiquo.scheduler.limit.VirtualMachinePrice.PricingModelVariables; import com.abiquo.scheduler.limit.VirtualMachinePrice.VirtualMachineCost; import com.abiquo.server.core.cloud.NodeVirtualImage; import com.abiquo.server.core.cloud.VirtualAppliance; import com.abiquo.server.core.cloud.VirtualApplianceDto; import com.abiquo.server.core.cloud.VirtualAppliancePriceDto; import com.abiquo.server.core.cloud.VirtualApplianceRep; import com.abiquo.server.core.cloud.VirtualApplianceState; import com.abiquo.server.core.cloud.VirtualDatacenter; import com.abiquo.server.core.cloud.VirtualDatacenterRep; import com.abiquo.server.core.cloud.VirtualMachine; import com.abiquo.server.core.cloud.VirtualMachineState; import com.abiquo.server.core.enterprise.Enterprise; import com.abiquo.server.core.enterprise.User; import com.abiquo.server.core.infrastructure.management.RasdManagement; import com.abiquo.server.core.infrastructure.management.RasdManagementDAO; import com.abiquo.server.core.infrastructure.storage.Tier; import com.abiquo.server.core.infrastructure.storage.VolumeManagement; import com.abiquo.server.core.pricing.CostCode; import com.abiquo.server.core.pricing.PricingCostCode; import com.abiquo.server.core.pricing.PricingRep; import com.abiquo.server.core.pricing.PricingTemplate; import com.abiquo.server.core.pricing.PricingTier; import com.abiquo.server.core.scheduler.VirtualMachineRequirements; import com.abiquo.server.core.task.Task; import com.abiquo.server.core.task.enums.TaskOwnerType; import com.abiquo.server.core.util.FilterOptions; import com.abiquo.tracer.ComponentType; import com.abiquo.tracer.EventType; import com.abiquo.tracer.SeverityType; /** * Implements the business logic of the class {@link VirtualAppliance} * * @author jdevesa@abiquo.com */ @Service public class VirtualApplianceService extends DefaultApiService { /** The logger object **/ private final static Logger logger = LoggerFactory.getLogger(VirtualApplianceService.class); @Autowired private TarantinoService tarantino; @Autowired private VirtualDatacenterRep repo; @Autowired protected VirtualDatacenterService vdcService; @Autowired protected UserService userService; @Autowired protected VirtualApplianceRep virtualApplianceRepo; @Autowired private PricingRep pricingRep; @Autowired private RasdManagementDAO rasdManDao; @Autowired private VirtualMachineService vmService; @Autowired private VirtualMachineRequirementsFactory requirements; @Autowired private VirtualMachineAllocatorService vmallocator; @Autowired private TaskService taskService; @Autowired private EnterpriseService entService; @Autowired private DatacenterService dcServbice; @Autowired SecurityService securityService; public VirtualApplianceService() { } public VirtualApplianceService(final EntityManager em) { this.repo = new VirtualDatacenterRep(em); this.vdcService = new VirtualDatacenterService(em); this.userService = new UserService(em); this.virtualApplianceRepo = new VirtualApplianceRep(em); this.pricingRep = new PricingRep(em); this.rasdManDao = new RasdManagementDAO(em); this.vmService = new VirtualMachineService(em); this.vmallocator = new VirtualMachineAllocatorService(em); this.requirements = new VirtualMachineRequirementsFactory(); this.securityService = new SecurityService(); } /** * Retrieves the list of virtual appliances by an unique virtual datacenter * * @param vdcId identifier of the virtualdatacenter. * @return the list of {@link VirtualAppliance} pojo */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<VirtualAppliance> getVirtualAppliancesByVirtualDatacenter(final Integer vdcId, final FilterOptions filterOptions) { VirtualDatacenter vdc = vdcService.getVirtualDatacenter(vdcId); return (List<VirtualAppliance>) repo.findVirtualAppliancesByVirtualDatacenter(vdc, filterOptions); } /** * Retrieves the list of virtual appliances by enterprise * * @param entId identifier of the enterprise. * @param filterOptions use to filter the results. <code>null</code> if no filtering required * @return the list of {@link VirtualAppliance} pojo */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<VirtualAppliance> getVirtualAppliancesByEnterprise(final Integer entId, final FilterOptions filterOptions) { Enterprise enterprise = entService.getEnterprise(entId); final User user = userService.getCurrentUser(); boolean findByUser = user != null && !securityService.canManageOtherEnterprises() && !securityService.canManageOtherUsers() && !StringUtils.isEmpty(user.getAvailableVirtualDatacenters()); if (findByUser) { return virtualApplianceRepo.findVirtualAppliancesByEnterprise(enterprise, filterOptions, user); } else { return virtualApplianceRepo.findVirtualAppliancesByEnterprise(enterprise, filterOptions, null); } } /** * Retrieves the list of virtual appliances by enterprise and datacenter * * @param entId identifier of the enterprise. * @param dcId identifier of the datacenter. * @return the list of {@link VirtualAppliance} pojo */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public List<VirtualAppliance> getVirtualAppliancesByEnterpriseAndDatacenter( final Integer entId, final Integer dcId) { entService.getEnterprise(entId); // checks enterprise dcServbice.getDatacenter(dcId); // checks datacenter final User user = userService.getCurrentUser(); boolean findByUser = user != null && !securityService.canManageOtherEnterprises() && !securityService.canManageOtherUsers() && !StringUtils.isEmpty(user.getAvailableVirtualDatacenters()); if (findByUser) { return virtualApplianceRepo.findVirtualAppliancesByEnterpriseAndDatacenter(entId, dcId, user); } else { return virtualApplianceRepo.findVirtualAppliancesByEnterpriseAndDatacenter(entId, dcId); } } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public VirtualAppliance getVirtualApplianceByVirtualMachine(final VirtualMachine virtualMachine) { return virtualApplianceRepo.findVirtualApplianceByVirtualMachine(virtualMachine); } /** * Returns the virtual appliance identi * * @param vdcId i * @param vappId * @return */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public VirtualAppliance getVirtualAppliance(final Integer vdcId, final Integer vappId) { VirtualDatacenter vdc = repo.findById(vdcId); if (vdc == null) { addNotFoundErrors(APIError.NON_EXISTENT_VIRTUAL_DATACENTER); flushErrors(); } if (vappId == 0) { addValidationErrors(APIError.INVALID_ID); flushErrors(); } VirtualAppliance vapp = repo.findVirtualApplianceById(vappId); if (vapp == null || !vapp.getVirtualDatacenter().getId().equals(vdcId)) { addNotFoundErrors(APIError.NON_EXISTENT_VIRTUALAPPLIANCE); flushErrors(); } return vapp; } /** * Returns the virtual appliance. * * @param vappId virtual appliance. * @return */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public VirtualAppliance getVirtualAppliance(final Integer vappId) { if (vappId == 0) { addValidationErrors(APIError.INVALID_ID); flushErrors(); } VirtualAppliance vapp = repo.findVirtualApplianceById(vappId); if (vapp == null) { addNotFoundErrors(APIError.NON_EXISTENT_VIRTUALAPPLIANCE); flushErrors(); } return vapp; } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public VirtualAppliance createVirtualAppliance(final Integer vdcId, final VirtualApplianceDto dto) { VirtualDatacenter vdc = vdcService.getVirtualDatacenter(vdcId); userService.checkCurrentEnterpriseForPostMethods(vdc.getEnterprise()); logger.debug("Create virtual appliance with name {}", dto.getName()); // Only empty virtual appliances can be created VirtualAppliance vapp = new VirtualAppliance(vdc.getEnterprise(), vdc, dto.getName(), VirtualApplianceState.NOT_DEPLOYED); vapp.setHighDisponibility(dto.getHighDisponibility()); vapp.setPublicApp(dto.getPublicApp()); vapp.setNodeconnections(dto.getNodeconnections()); if (!vapp.isValid()) { StringBuilder sb = extractErrorsInAString(vapp); logger.error( "Error create virtual appliance with name {} due to validation errors: {}", dto .getName(), sb.toString()); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_APPLIANCE, EventType.VAPP_CREATE, "virtualAppliance.deleteErrorValidations", dto.getName()); addValidationErrors(vapp.getValidationErrors()); flushErrors(); } logger.debug("Add virtual appliance to Abiquo with name {}", dto.getName()); repo.insertVirtualAppliance(vapp); logger.debug("Created virtual appliance with name {} !", dto.getName()); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_APPLIANCE, EventType.VAPP_CREATE, "virtualAppliance.created", dto.getName()); return vapp; } private StringBuilder extractErrorsInAString(final VirtualAppliance vapp) { Set<CommonError> errors = vapp.getValidationErrors(); StringBuilder sb = new StringBuilder(); for (CommonError e : errors) { sb.append("Error code: ").append(e.getCode()).append(", Message: ").append( e.getMessage()); } return sb; } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public VirtualAppliance updateVirtualAppliance(final Integer vdcId, final Integer vappId, final VirtualApplianceDto dto) { VirtualAppliance vapp = getVirtualAppliance(vdcId, vappId); userService.checkCurrentEnterpriseForPostMethods(vapp.getEnterprise()); vapp.setName(dto.getName()); vapp.setNodeconnections(dto.getNodeconnections()); repo.updateVirtualAppliance(vapp); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_APPLIANCE, EventType.VAPP_MODIFY, "virtualAppliance.modify", vapp.getName()); return vapp; } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String getPriceVirtualApplianceText(final Integer vdcId, final Integer vappId) { String price = ""; VirtualAppliance virtualAppliance = getVirtualAppliance(vdcId, vappId); // if enterprise has pt associated PricingTemplate pricingTemplate = virtualAppliance.getEnterprise().getPricingTemplate(); if (pricingTemplate != null && pricingTemplate.isShowChangesBefore()) { VirtualAppliancePriceDto priceDto = getPriceVirtualAppliance(virtualAppliance, pricingTemplate); price = pricingTemplate.getDescription(); price = price.replace(PricingModelVariables.CHARGE.getText(), priceDto.getTotalCost() + " " + pricingTemplate.getCurrency().getSymbol()); price = price.replace(PricingModelVariables.CHARGE_PERIOD.getText(), pricingTemplate .getChargingPeriod().name()); price = price.replace(PricingModelVariables.MIN_CHARGE.getText(), priceDto .getMinimumChargePeriod() + " " + pricingTemplate.getCurrency().getSymbol()); price = price.replace(PricingModelVariables.MIN_PERIOD.getText(), pricingTemplate .getMinimumCharge().name()); } if (!price.equals("")) { price = price + "\n"; } return price;// + "\n"; } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public VirtualAppliancePriceDto getPriceVirtualAppliance( final VirtualAppliance virtualAppliance, final PricingTemplate pricingTemplate) { BigDecimal cost = new BigDecimal(0); Map<VirtualMachineCost, BigDecimal> virtualMachinesCost = new HashMap<VirtualMachinePrice.VirtualMachineCost, BigDecimal>(); virtualMachinesCost.put(VirtualMachineCost.COMPUTE, cost); virtualMachinesCost.put(VirtualMachineCost.COST_CODE, cost); virtualMachinesCost.put(VirtualMachineCost.NETWORK, cost); virtualMachinesCost.put(VirtualMachineCost.ADDITIONAL_VOLUME, cost); virtualMachinesCost.put(VirtualMachineCost.STORAGE, cost); virtualMachinesCost.put(VirtualMachineCost.STANDING_CHARGE, cost); virtualMachinesCost.put(VirtualMachineCost.TOTAL, cost); VirtualAppliancePriceDto dto = new VirtualAppliancePriceDto(cost, cost, cost, cost, cost, cost); int significantDigits = pricingTemplate.getCurrency().getDigits(); for (NodeVirtualImage node : virtualAppliance.getNodes()) { final VirtualMachine vmachine = node.getVirtualMachine(); VirtualMachineRequirements virtualMachineRequirements = requirements.createVirtualMachineRequirements(vmachine); virtualMachinesCost = addVirtualMachineCost(virtualMachinesCost, node.getVirtualMachine(), virtualMachineRequirements, pricingTemplate); } dto.setAdditionalVolumCost(rounded(significantDigits, virtualMachinesCost .get(VirtualMachineCost.ADDITIONAL_VOLUME))); dto.setCostCodeCost(rounded(significantDigits, virtualMachinesCost .get(VirtualMachineCost.COST_CODE))); dto.setComputeCost(rounded(significantDigits, virtualMachinesCost .get(VirtualMachineCost.COMPUTE))); dto.setStorageCost(rounded(significantDigits, virtualMachinesCost .get(VirtualMachineCost.STORAGE))); dto.setNetworkCost(rounded(significantDigits, virtualMachinesCost .get(VirtualMachineCost.NETWORK))); dto .setStandingCharge(rounded(significantDigits, pricingTemplate.getStandingChargePeriod())); dto.setMinimumCharge(pricingTemplate.getMinimumCharge().ordinal()); dto.setMinimumChargePeriod(rounded(significantDigits, pricingTemplate .getMinimumChargePeriod())); dto.setTotalCost(rounded(significantDigits, virtualMachinesCost .get(VirtualMachineCost.TOTAL))); // It is for enterprise so we don't have to add to the price // .add( pricingTemplate.getStandingChargePeriod()) return dto; } private BigDecimal rounded(final int significantDigits, final BigDecimal aNumber) { return aNumber.setScale(significantDigits, BigDecimal.ROUND_UP); } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) private Map<VirtualMachineCost, BigDecimal> addVirtualMachineCost( final Map<VirtualMachineCost, BigDecimal> virtualMachinesCost, final VirtualMachine virtualMachine, final VirtualMachineRequirements virtualMachineRequirements, final PricingTemplate pricingTemplate) { BigDecimal BYTES_TO_GB = new BigDecimal(1024l * 1024l * 1024l); BigDecimal MB_TO_GB = new BigDecimal(1024); getCostCodeCost(virtualMachinesCost, virtualMachine, pricingTemplate); Collection<RasdManagement> resources = rasdManDao.findByVirtualMachine(virtualMachine); getAdditionalStorageCost(virtualMachinesCost, resources, pricingTemplate); virtualMachinesCost.put(VirtualMachineCost.COMPUTE, virtualMachinesCost.get( VirtualMachineCost.COMPUTE) .add( pricingTemplate.getVcpu().multiply( new BigDecimal(virtualMachineRequirements.getCpu())))); virtualMachinesCost.put(VirtualMachineCost.COMPUTE, virtualMachinesCost.get( VirtualMachineCost.COMPUTE).add( pricingTemplate.getMemoryGB().multiply( new BigDecimal(virtualMachineRequirements.getRam()).divide(MB_TO_GB)))); virtualMachinesCost.put(VirtualMachineCost.STORAGE, virtualMachinesCost.get( VirtualMachineCost.STORAGE).add( pricingTemplate.getHdGB().multiply( new BigDecimal(virtualMachineRequirements.getHd()).divide(BYTES_TO_GB, 2, BigDecimal.ROUND_HALF_EVEN)))); virtualMachinesCost.put(VirtualMachineCost.NETWORK, virtualMachinesCost.get( VirtualMachineCost.NETWORK).add( pricingTemplate.getPublicIp().multiply( new BigDecimal(virtualMachineRequirements.getPublicIP())))); virtualMachinesCost.put(VirtualMachineCost.TOTAL, virtualMachinesCost.get( VirtualMachineCost.COST_CODE).add( virtualMachinesCost.get(VirtualMachineCost.COMPUTE).add( virtualMachinesCost.get(VirtualMachineCost.STORAGE).add( virtualMachinesCost.get(VirtualMachineCost.ADDITIONAL_VOLUME).add( virtualMachinesCost.get(VirtualMachineCost.NETWORK)))))); return virtualMachinesCost; } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) private void getCostCodeCost(final Map<VirtualMachineCost, BigDecimal> virtualMachinesCost, final VirtualMachine virtualMachine, final PricingTemplate pricing) { CostCode cc = pricingRep.findCostCodeById(virtualMachine.getVirtualMachineTemplate().getCostCode()); PricingCostCode pricingCostCode = pricingRep.findPricingCostCode(cc, pricing); if (pricingCostCode != null) { virtualMachinesCost.put(VirtualMachineCost.COST_CODE, virtualMachinesCost.get( VirtualMachineCost.COST_CODE).add(pricingCostCode.getPrice())); } } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) private void getAdditionalStorageCost( final Map<VirtualMachineCost, BigDecimal> virtualMachinesCost, final Collection<RasdManagement> resources, final PricingTemplate pricing) { for (final RasdManagement resource : resources) { if (resource instanceof VolumeManagement) { final VolumeManagement volman = (VolumeManagement) resource; // accum += volman.getSizeInMB(); Tier tier = pricingRep.findTierById(volman.getStoragePool().getTier().getId()); PricingTier pricingTier = pricingRep.findPricingTier(tier, pricing); if (pricingTier != null) { BigDecimal volum = new BigDecimal(volman.getSizeInMB()); BigDecimal toGB = new BigDecimal(1024); virtualMachinesCost.put(VirtualMachineCost.ADDITIONAL_VOLUME, virtualMachinesCost.get(VirtualMachineCost.ADDITIONAL_VOLUME).add( pricingTier.getPrice().multiply(volum.divide(toGB))));// multiplicar // por // _MB } } } } private void allocateVirtualAppliance(final VirtualAppliance vapp, final boolean foreceEnterpriseSoftLimits) { VirtualMachineRequirements required = requirements.createVirtualMachineRequirements(vapp); vmallocator.checkLimist(vapp, required, foreceEnterpriseSoftLimits); try { for (NodeVirtualImage nvi : vapp.getNodes()) { final VirtualMachine virtualMachine = nvi.getVirtualMachine(); // XXX duplicated limit checker vmService.allocate(virtualMachine, vapp, foreceEnterpriseSoftLimits); } // XXX consider (try to) having the SchechedulerLock by each VM // for (Integer vmid : nviDao.findVirtualMachineIdsByVirtualAppliance(vapp)) // { final String msg = String.format("Allocate %d", vmid); try { // SchedulerLock.acquire(msg); final VirtualMachine virtualMachine = // vmService.getVirtualMachine(vmid); vmService.allocate(virtualMachine, vapp, // foreceEnterpriseSoftLimits); } finally { SchedulerLock.release(msg); } } } catch (APIException e) { for (NodeVirtualImage nvi : vapp.getNodes()) { VirtualMachine vm = nvi.getVirtualMachine(); if (vm.getState() == VirtualMachineState.ALLOCATED) { vmallocator.deallocateVirtualMachine(vm); // TODO consider moving the set NOT_ALLOCATED in deallocateVM vm.setState(VirtualMachineState.NOT_ALLOCATED); vmService.updateVirtualMachineBySystem(vm); } } throw e; } catch (RuntimeException e) { for (NodeVirtualImage nvi : vapp.getNodes()) { VirtualMachine vm = nvi.getVirtualMachine(); if (vm.getState() == VirtualMachineState.ALLOCATED) { vmallocator.deallocateVirtualMachine(vm); // TODO consider moving the set NOT_ALLOCATED in deallocateVM vm.setState(VirtualMachineState.NOT_ALLOCATED); vmService.updateVirtualMachineBySystem(vm); } } addUnexpectedErrors(APIError.STATUS_INTERNAL_SERVER_ERROR); flushErrors(); } } /** * Deploys all of the {@link VirtualMachine} belonging to this {@link VirtualAppliance} * <p> * TODO force TRUE * * @param vdcId {@link VirtualDatacenter} * @param vappId {@link VirtualAppliance} * @return List<String> */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public Map<Integer, String> deployVirtualAppliance(final Integer vdcId, final Integer vappId, final boolean forceLimits) { VirtualAppliance virtualAppliance = getVirtualAppliance(vdcId, vappId); List<NodeVirtualImage> vappNodes = virtualAppliance.getNodes(); if (vappNodes.isEmpty()) { addConflictErrors(APIError.VIRTUALAPPLIANCE_EMPTY); flushErrors(); } allocateVirtualAppliance(virtualAppliance, forceLimits); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_APPLIANCE, EventType.VAPP_POWERON, "virtualAppliance.deploy", virtualAppliance.getName()); Map<Integer, String> dto = new HashMap<Integer, String>(); for (NodeVirtualImage nodevi : vappNodes) { VirtualMachine vmachine = nodevi.getVirtualMachine(); try { String link = vmService.sendDeploy(vmachine, virtualAppliance); dto.put(vmachine.getId(), link); } catch (Exception e) { logger .error( "Error already logged in the sendDeploy deploying virtual appliance name {}. {}", virtualAppliance.getName(), e.toString()); } } return dto; } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public VirtualApplianceState getVirtualApplianceState(final Integer vdcId, final Integer vappId) { VirtualAppliance virtualAppliance = getVirtualAppliance(vdcId, vappId); return virtualAppliance.getState(); } @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public Map<Integer, String> undeployVirtualAppliance(final Integer vdcId, final Integer vappId, final Boolean forceUndeploy, final Map<Integer, VirtualMachineState> originalStates) { VirtualAppliance virtualAppliance = getVirtualAppliance(vdcId, vappId); // first check if there is any imported virtualmachine. if (!forceUndeploy) { for (NodeVirtualImage node : virtualAppliance.getNodes()) { if (node.getVirtualMachine().isCaptured()) { addConflictErrors(APIError.VIRTUAL_MACHINE_IMPORTED_WILL_BE_DELETED); flushErrors(); } } } tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_APPLIANCE, EventType.VAPP_POWEROFF, "virtualAppliance.undeploy", virtualAppliance.getName()); Map<Integer, String> dto = new HashMap<Integer, String>(); for (NodeVirtualImage machine : virtualAppliance.getNodes()) { Integer vmId = machine.getVirtualMachine().getId(); try { String link = vmService.undeployVirtualMachine(vmId, vappId, vdcId, forceUndeploy, originalStates.get(vmId)); dto.put(machine.getVirtualMachine().getId(), link); } catch (Exception e) { // The virtual appliance is in an unknown state tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_APPLIANCE, EventType.VM_UNDEPLOY, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_APPLIANCE, EventType.VM_UNDEPLOY, "virtualAppliance.deployError", e.toString(), machine .getName(), e.getMessage()); logger .error( "Error undeploying virtual appliance name {}. But we continue with next virtual machine: {}", virtualAppliance.getName(), e.toString()); } } return dto; } @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void deleteVirtualAppliance(final Integer vdcId, final Integer vappId, final Map<Integer, VirtualMachineState> originalStates) { VirtualAppliance virtualAppliance = getVirtualAppliance(vdcId, vappId); userService.checkCurrentEnterpriseForPostMethods(virtualAppliance.getEnterprise()); logger.debug("Deleting the virtual appliance name {} ", virtualAppliance.getName()); // We must delete all of its virtual machines for (NodeVirtualImage n : virtualAppliance.getNodes()) { Integer vmId = n.getVirtualMachine().getId(); logger.trace("Deleting the virtual machine with name {}", n.getVirtualMachine() .getName()); vmService.deleteVirtualMachine(vmId, virtualAppliance.getId(), n.getVirtualAppliance() .getVirtualDatacenter().getId(), originalStates.get(vmId)); logger.trace("Deleting the virtual machine with name {}", n.getVirtualMachine() .getName() + " successful!"); } virtualApplianceRepo.deleteVirtualAppliance(virtualAppliance); logger.debug("Deleting the virtual appliance name {} ok!", virtualAppliance.getName()); tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_APPLIANCE, EventType.VAPP_DELETE, "virtualAppliance.deleted", virtualAppliance.getName()); } /** * Retrieve the last {@link Task} of every {@link VirtualMachine} in the * {@link VirtualAppliance} * * @param vdcId datacenter id. * @param vappId virtualappliance id * @return List<Task> */ public List<Task> getAllNodesLastTask(final Integer vdcId, final Integer vappId) { VirtualAppliance virtualAppliance = getVirtualAppliance(vdcId, vappId); logger.debug("Retrieving all of the nodes last task virtual appliance name {} ", virtualAppliance.getName()); List<Task> tasks = new ArrayList<Task>(); for (NodeVirtualImage m : virtualAppliance.getNodes()) { List<Task> t = taskService.findTasks(TaskOwnerType.VIRTUAL_MACHINE, m.getVirtualMachine().getId() .toString()); if (t != null && !t.isEmpty()) { tasks.add(t.get(0)); } } logger.debug( "Retrieving all of the nodes last task virtual appliance name {}, added {} tasks ", new Object[] {virtualAppliance.getName(), tasks.size()}); return tasks; } }