package com.cloud.deploy; import java.util.ArrayList; import java.util.List; import javax.ejb.Local; import org.apache.log4j.Logger; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.HostPodVO; import com.cloud.dc.Pod; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.host.DetailVO; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.DetailsDao; import com.cloud.host.dao.HostDao; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; import com.cloud.storage.GuestOSCategoryVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSDao; import com.cloud.template.VirtualMachineTemplate; import com.cloud.utils.component.Inject; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @Local(value=DeploymentPlanner.class) public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { private static final Logger s_logger = Logger.getLogger(FirstFitPlanner.class); @Inject private HostDao _hostDao; @Inject private CapacityDao _capacityDao; @Inject private DataCenterDao _dcDao; @Inject private HostPodDao _podDao; @Inject private ClusterDao _clusterDao; @Inject DetailsDao _hostDetailsDao = null; @Inject GuestOSDao _guestOSDao = null; @Inject GuestOSCategoryDao _guestOSCategoryDao = null; @Override public DeployDestination plan(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException { VirtualMachine vm = vmProfile.getVirtualMachine(); ServiceOffering offering = vmProfile.getServiceOffering(); DataCenter dc = _dcDao.findById(vm.getDataCenterId()); int cpu_requested = offering.getCpu() * offering.getSpeed(); long ram_requested = offering.getRamSize() * 1024L * 1024L; s_logger.debug("try to allocate a host from dc:" + plan.getDataCenterId() + ", pod:" + plan.getPodId() + ",cluster:" + plan.getClusterId() + ", requested cpu: " + cpu_requested + ", requested ram: " + ram_requested); if (vm.getLastHostId() != null) { HostVO host = _hostDao.findById(vm.getLastHostId()); if (host != null && host.getStatus() == Status.Up) { boolean canDepployToLastHost = deployToHost(host, cpu_requested, ram_requested, true, avoid); if (canDepployToLastHost) { Pod pod = _podDao.findById(vm.getPodId()); Cluster cluster = _clusterDao.findById(host.getClusterId()); return new DeployDestination(dc, pod, cluster, host); } } } /*Go through all the pods/clusters under zone*/ List pods = null; if (plan.getPodId() != null) { HostPodVO pod = _podDao.findById(plan.getPodId()); if (pod != null && dc.getId() == pod.getDataCenterId()) { pods = new ArrayList(1); pods.add(pod); } else { s_logger.debug("Can't enforce the pod selector"); return null; } } if (pods == null) pods = _podDao.listByDataCenterId(plan.getDataCenterId()); //Collections.shuffle(pods); for (HostPodVO hostPod : pods) { if (avoid.shouldAvoid(hostPod)) { continue; } //Collections.shuffle(clusters); List clusters = null; if (plan.getClusterId() != null) { ClusterVO cluster = _clusterDao.findById(plan.getClusterId()); if (cluster != null && hostPod.getId() == cluster.getPodId()) { clusters = new ArrayList(1); clusters.add(cluster); } else { s_logger.debug("Can't enforce the cluster selector"); return null; } } if (clusters == null) { clusters = _clusterDao.listByPodId(hostPod.getId()); } for (ClusterVO clusterVO : clusters) { if (avoid.shouldAvoid(clusterVO)) { continue; } if (clusterVO.getHypervisorType() != vmProfile.getHypervisorType()) { avoid.addCluster(clusterVO.getId()); continue; } List hosts = _hostDao.listBy(Host.Type.Routing, clusterVO.getId(), hostPod.getId(), dc.getId()); //Collections.shuffle(hosts); // We will try to reorder the host lists such that we give priority to hosts that have // the minimums to support a VM's requirements hosts = prioritizeHosts(vmProfile.getTemplate(), hosts); for (HostVO hostVO : hosts) { boolean canDeployToHost = deployToHost(hostVO, cpu_requested, ram_requested, false, avoid); if (canDeployToHost) { Pod pod = _podDao.findById(hostPod.getId()); Cluster cluster = _clusterDao.findById(clusterVO.getId()); Host host = _hostDao.findById(hostVO.getId()); return new DeployDestination(dc, pod, cluster, host); } avoid.addHost(hostVO.getId()); } avoid.addCluster(clusterVO.getId()); } avoid.addPod(hostPod.getId()); } return null; } @Override public boolean check(VirtualMachineProfile vm, DeploymentPlan plan, DeployDestination dest, ExcludeList exclude) { // TODO Auto-generated method stub return false; } @DB protected boolean deployToHost(HostVO host, Integer cpu, long ram, boolean fromLastHost, ExcludeList avoid) { if (avoid.shouldAvoid(host)) { return false; } CapacityVO capacityCpu = _capacityDao.findByHostIdType(host.getId(), CapacityVO.CAPACITY_TYPE_CPU); CapacityVO capacityMem = _capacityDao.findByHostIdType(host.getId(), CapacityVO.CAPACITY_TYPE_MEMORY); Transaction txn = Transaction.currentTxn(); try { txn.start(); capacityCpu = _capacityDao.lockRow(capacityCpu.getId(), true); capacityMem = _capacityDao.lockRow(capacityMem.getId(), true); long usedCpu = capacityCpu.getUsedCapacity(); long usedMem = capacityMem.getUsedCapacity(); long reservedCpu = capacityCpu.getReservedCapacity(); long reservedMem = capacityMem.getReservedCapacity(); long totalCpu = capacityCpu.getTotalCapacity(); long totalMem = capacityMem.getTotalCapacity(); boolean success = false; if (fromLastHost) { /*alloc from reserved*/ if (reservedCpu >= cpu && reservedMem >= ram) { capacityCpu.setReservedCapacity(reservedCpu - cpu); capacityMem.setReservedCapacity(reservedMem - ram); capacityCpu.setUsedCapacity(usedCpu + cpu); capacityMem.setUsedCapacity(usedMem + ram); success = true; } } else { /*alloc from free resource*/ if ((reservedCpu + usedCpu + cpu <= totalCpu) && (reservedMem + usedMem + ram <= totalMem)) { capacityCpu.setUsedCapacity(usedCpu + cpu); capacityMem.setUsedCapacity(usedMem + ram); success = true; } } if (success) { _capacityDao.update(capacityCpu.getId(), capacityCpu); _capacityDao.update(capacityMem.getId(), capacityMem); } txn.commit(); return success; } catch (Exception e) { txn.rollback(); return false; } } protected List prioritizeHosts(VirtualMachineTemplate template, List hosts) { if (template == null) { return hosts; } // Determine the guest OS category of the template String templateGuestOSCategory = getTemplateGuestOSCategory(template); List prioritizedHosts = new ArrayList(); // If a template requires HVM and a host doesn't support HVM, remove it from consideration List hostsToCheck = new ArrayList(); if (template.isRequiresHvm()) { for (HostVO host : hosts) { if (hostSupportsHVM(host)) { hostsToCheck.add(host); } } } else { hostsToCheck.addAll(hosts); } // If a host is tagged with the same guest OS category as the template, move it to a high priority list // If a host is tagged with a different guest OS category than the template, move it to a low priority list List highPriorityHosts = new ArrayList(); List lowPriorityHosts = new ArrayList(); for (HostVO host : hostsToCheck) { String hostGuestOSCategory = getHostGuestOSCategory(host); if (hostGuestOSCategory == null) { continue; } else if (templateGuestOSCategory.equals(hostGuestOSCategory)) { highPriorityHosts.add(host); } else { lowPriorityHosts.add(host); } } hostsToCheck.removeAll(highPriorityHosts); hostsToCheck.removeAll(lowPriorityHosts); // Prioritize the remaining hosts by HVM capability for (HostVO host : hostsToCheck) { if (!template.isRequiresHvm() && !hostSupportsHVM(host)) { // Host and template both do not support hvm, put it as first consideration prioritizedHosts.add(0, host); } else { // Template doesn't require hvm, but the machine supports it, make it last for consideration prioritizedHosts.add(host); } } // Merge the lists prioritizedHosts.addAll(0, highPriorityHosts); prioritizedHosts.addAll(lowPriorityHosts); return prioritizedHosts; } protected boolean hostSupportsHVM(HostVO host) { // Determine host capabilities String caps = host.getCapabilities(); if (caps != null) { String[] tokens = caps.split(","); for (String token : tokens) { if (token.contains("hvm")) { return true; } } } return false; } protected String getHostGuestOSCategory(HostVO host) { DetailVO hostDetail = _hostDetailsDao.findDetail(host.getId(), "guest.os.category.id"); if (hostDetail != null) { String guestOSCategoryIdString = hostDetail.getValue(); long guestOSCategoryId; try { guestOSCategoryId = Long.parseLong(guestOSCategoryIdString); } catch (Exception e) { return null; } GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); if (guestOSCategory != null) { return guestOSCategory.getName(); } else { return null; } } else { return null; } } protected String getTemplateGuestOSCategory(VirtualMachineTemplate template) { long guestOSId = template.getGuestOSId(); GuestOSVO guestOS = _guestOSDao.findById(guestOSId); long guestOSCategoryId = guestOS.getCategoryId(); GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); return guestOSCategory.getName(); } }