/** * 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.scheduler; import java.util.Collections; import java.util.LinkedList; import java.util.List; import javax.persistence.EntityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.abiquo.api.config.ConfigService; import com.abiquo.api.services.config.SystemPropertyService; import com.abiquo.scheduler.workload.NotEnoughResourcesException; import com.abiquo.server.core.cloud.Hypervisor; import com.abiquo.server.core.cloud.VirtualMachine; import com.abiquo.server.core.config.SystemProperty; import com.abiquo.server.core.infrastructure.Datastore; import com.abiquo.server.core.infrastructure.InfrastructureRep; import com.abiquo.server.core.infrastructure.Machine; /** * Builds a VirtualMachine from a PhysicalMachine and a VirtualImage. * * @transactional required-read only */ /** * TODO this should be a @Repository */ @Component public class VirtualMachineFactory { /** Use an invalid port to indicate a disabled vrdp. */ protected static final int DISABLED_VRDPORT = 65535 + 10; private final static Logger log = LoggerFactory.getLogger(VirtualMachineFactory.class); @Autowired protected InfrastructureRep datacenterRepo; @Autowired private SystemPropertyService systemPropertyService; public VirtualMachineFactory() { } public VirtualMachineFactory(final EntityManager em) { this.datacenterRepo = new InfrastructureRep(em); this.systemPropertyService = new SystemPropertyService(em); } /** The remote desktop min port **/ public final static int MIN_REMOTE_DESKTOP_PORT = Integer.valueOf(ConfigService .getSystemProperty(ConfigService.MIN_REMOTE_DESKTOP_PORT, "5900")); public final static int MAX_REMOTE_DESKTOP_PORT = Integer.valueOf(ConfigService .getSystemProperty(ConfigService.MAX_REMOTE_DESKTOP_PORT, "65534")); protected final static String ALLOW_RDP_PROPERTY = "client.virtual.allowVMRemoteAccess"; /** * Create a Virtual Machine on the given PhysicalMachine to deploy the given * VirtualMachineTemplate. * * @param machine, the machine hypervisor will be used to create the new virtual machine * template. * @return a new VirtualMachine instance inside physical to load image. * <p> * TODO: creating default Hypervisor instance * <p> * TODO: VdrpIP, VdrpPort The hypervisors shall be discovered when the physical machine * are loaded so we recover the hypervisors from the DB * @throws NotEnoughResourcesException, if the target machine haven't enough resources to hold * the virtual machine */ @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public VirtualMachine createVirtualMachine(final Machine machine, final VirtualMachine virtualMachine) throws NotEnoughResourcesException { // TODO set UUID and name // TODO default also high disponibility flag) // To define a new UUID when the VM is ready to be instanced decomment the line below // virtualMachine.setUUID(UUID.randomUUID().toString()); // virtualMachine.setDescription(image.getDescription()); // same description as the vimage final Hypervisor hypervisor = machine.getHypervisor(); virtualMachine.setHypervisor(hypervisor); if (virtualMachine.getDatastore() == null) { final long datastoreRequ = virtualMachine.getVirtualMachineTemplate().getDiskFileSize(); final Datastore datastore = selectDatastore(machine, datastoreRequ); virtualMachine.setDatastore(datastore); virtualMachine.setVdrpPort(isRemoteAccessEnabled() ? selectVrdpPort(machine) : DISABLED_VRDPORT); } else // its an HA reallocation, the datastore was already { final String currentDatastoreUuid = virtualMachine.getDatastore().getDatastoreUUID(); // find the shared datastore on the target machine Datastore datastore = datacenterRepo.findDatastoreByUuidAndMachine(currentDatastoreUuid, machine); virtualMachine.setDatastore(datastore); } virtualMachine.setVdrpIP(hypervisor.getIpService()); return virtualMachine; } /** * Selects the larger datastore from the physical machine datastore list * * @param physical the physical machine * @param session * @return the target datastore where the virtual machine will be deployed * @throws SchedulerException */ private Datastore selectDatastore(final Machine machine, final Long hdDiskRequired) throws NotEnoughResourcesException { List<Datastore> datastores = datacenterRepo.findMachineDatastores(machine); if (datastores.isEmpty()) { final String cause = "The target physical machine has no datastores."; throw new NotEnoughResourcesException(cause); } // Getting the enabled datastores Long freeLargerSize = 0L; Datastore betterDatastore = null; for (final Datastore datastore : datastores) { if (datastore.isEnabled()) { final Long currentLargerSize = datastore.getSize() - datastore.getUsedSize(); if (freeLargerSize < currentLargerSize) { freeLargerSize = currentLargerSize; betterDatastore = datastore; } } } if (betterDatastore == null || freeLargerSize < hdDiskRequired) { final String cause = "The target physical machine has no datastores enabled with the required free size."; throw new NotEnoughResourcesException(cause); } // // updating datastore XXX set on resourceupgradeuse // betterDatastore.setUsedSize(betterDatastore.getUsedSize() + hdImageRequired); // session.update(dsHB); log.debug("The selected datastore for deploying is: {}", betterDatastore.getName()); return betterDatastore; } protected int selectVrdpPort(final Machine machine) throws NotEnoughResourcesException { final List<Integer> usedPorts = datacenterRepo.findUsedRemoteDesktopPortsInRack(machine.getRack()); Integer candidatePort = getNextFreeRemoteDesktopPort(usedPorts); log.debug("The assigned remote desktop port is: {}", candidatePort); return candidatePort; } /** * Gets a free port from the list used port * * @param portsInUse * @return * @throws SchedulerException */ protected Integer getNextFreeRemoteDesktopPort(final List<Integer> portsInUse) throws NotEnoughResourcesException { // Sort ports (we don't care about portsInUse having repeated elements) Collections.sort(portsInUse); List<Integer> allowedPorts = new LinkedList<Integer>(); for (int i = MIN_REMOTE_DESKTOP_PORT; i <= MAX_REMOTE_DESKTOP_PORT; i++) { allowedPorts.add(i); } allowedPorts.removeAll(portsInUse); if (allowedPorts.isEmpty()) { throw new NotEnoughResourcesException("The maximun number of remote desktop ports has been reached"); } return allowedPorts.get(0); } private boolean isRemoteAccessEnabled() { SystemProperty allowRdp = systemPropertyService.findByName(ALLOW_RDP_PROPERTY); return allowRdp == null || !allowRdp.getValue().startsWith("0"); } }