// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.agent.manager.allocator.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.manager.allocator.HostAllocator; import com.cloud.capacity.CapacityManager; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.host.DetailVO; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostDetailsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.offering.ServiceOffering; import com.cloud.resource.ResourceManager; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.GuestOSCategoryVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSDao; import com.cloud.user.Account; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.SecondaryStorageVmDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; /** * An allocator that tries to find a fit on a computing host. This allocator does not care whether or not the host supports routing. */ @Local(value={HostAllocator.class}) public class FirstFitAllocator implements HostAllocator { private static final Logger s_logger = Logger.getLogger(FirstFitAllocator.class); private String _name; @Inject HostDao _hostDao = null; @Inject HostDetailsDao _hostDetailsDao = null; @Inject UserVmDao _vmDao = null; @Inject ServiceOfferingDao _offeringDao = null; @Inject DomainRouterDao _routerDao = null; @Inject ConsoleProxyDao _consoleProxyDao = null; @Inject SecondaryStorageVmDao _secStorgaeVmDao = null; @Inject ConfigurationDao _configDao = null; @Inject GuestOSDao _guestOSDao = null; @Inject GuestOSCategoryDao _guestOSCategoryDao = null; @Inject HypervisorCapabilitiesDao _hypervisorCapabilitiesDao = null; @Inject VMInstanceDao _vmInstanceDao = null; @Inject ResourceManager _resourceMgr; float _factor = 1; protected String _allocationAlgorithm = "random"; @Inject CapacityManager _capacityMgr; @Override public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo) { return allocateTo(vmProfile, plan, type, avoid, returnUpTo, true); } @Override public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) { long dcId = plan.getDataCenterId(); Long podId = plan.getPodId(); Long clusterId = plan.getClusterId(); ServiceOffering offering = vmProfile.getServiceOffering(); VMTemplateVO template = (VMTemplateVO)vmProfile.getTemplate(); Account account = vmProfile.getOwner(); if (type == Host.Type.Storage) { // FirstFitAllocator should be used for user VMs only since it won't care whether the host is capable of routing or not return new ArrayList<Host>(); } if(s_logger.isDebugEnabled()){ s_logger.debug("Looking for hosts in dc: " + dcId + " pod:" + podId + " cluster:" + clusterId ); } String hostTagOnOffering = offering.getHostTag(); String hostTagOnTemplate = template.getTemplateTag(); boolean hasSvcOfferingTag = hostTagOnOffering != null ? true : false; boolean hasTemplateTag = hostTagOnTemplate != null ? true : false; List<HostVO> clusterHosts = new ArrayList<HostVO>(); String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag); if (haVmTag != null) { clusterHosts = _hostDao.listByHostTag(type, clusterId, podId, dcId, haVmTag); } else { if (hostTagOnOffering == null && hostTagOnTemplate == null){ clusterHosts = _resourceMgr.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId); } else { List<HostVO> hostsMatchingOfferingTag = new ArrayList<HostVO>(); List<HostVO> hostsMatchingTemplateTag = new ArrayList<HostVO>(); if (hasSvcOfferingTag){ if (s_logger.isDebugEnabled()){ s_logger.debug("Looking for hosts having tag specified on SvcOffering:" + hostTagOnOffering); } hostsMatchingOfferingTag = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnOffering); if (s_logger.isDebugEnabled()){ s_logger.debug("Hosts with tag '" + hostTagOnOffering + "' are:" + hostsMatchingOfferingTag); } } if (hasTemplateTag){ if (s_logger.isDebugEnabled()){ s_logger.debug("Looking for hosts having tag specified on Template:" + hostTagOnTemplate); } hostsMatchingTemplateTag = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate); if (s_logger.isDebugEnabled()){ s_logger.debug("Hosts with tag '" + hostTagOnTemplate+"' are:" + hostsMatchingTemplateTag); } } if (hasSvcOfferingTag && hasTemplateTag){ hostsMatchingOfferingTag.retainAll(hostsMatchingTemplateTag); clusterHosts = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate); if (s_logger.isDebugEnabled()){ s_logger.debug("Found "+ hostsMatchingOfferingTag.size() +" Hosts satisfying both tags, host ids are:" + hostsMatchingOfferingTag); } clusterHosts = hostsMatchingOfferingTag; } else { if (hasSvcOfferingTag){ clusterHosts = hostsMatchingOfferingTag; } else { clusterHosts = hostsMatchingTemplateTag; } } } } return allocateTo(plan, offering, template, avoid, clusterHosts, returnUpTo, considerReservedCapacity, account); } protected List<Host> allocateTo(DeploymentPlan plan, ServiceOffering offering, VMTemplateVO template, ExcludeList avoid, List<HostVO> hosts, int returnUpTo, boolean considerReservedCapacity, Account account) { if (_allocationAlgorithm.equals("random") || _allocationAlgorithm.equals("userconcentratedpod_random")) { // Shuffle this so that we don't check the hosts in the same order. Collections.shuffle(hosts); }else if(_allocationAlgorithm.equals("userdispersing")){ hosts = reorderHostsByNumberOfVms(plan, hosts, account); } if (s_logger.isDebugEnabled()) { s_logger.debug("FirstFitAllocator has " + hosts.size() + " hosts to check for allocation: "+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(template, hosts); if (s_logger.isDebugEnabled()) { s_logger.debug("Found " + hosts.size() + " hosts for allocation after prioritization: "+ hosts); } if (s_logger.isDebugEnabled()) { s_logger.debug("Looking for speed=" + (offering.getCpu() * offering.getSpeed()) + "Mhz, Ram=" + offering.getRamSize()); } List<Host> suitableHosts = new ArrayList<Host>(); for (HostVO host : hosts) { if(suitableHosts.size() == returnUpTo){ break; } if (avoid.shouldAvoid(host)) { if (s_logger.isDebugEnabled()) { s_logger.debug("Host name: " + host.getName() + ", hostId: "+ host.getId() +" is in avoid set, skipping this and trying other available hosts"); } continue; } //find number of guest VMs occupying capacity on this host. Long vmCount = _vmInstanceDao.countRunningByHostId(host.getId()); Long maxGuestLimit = getHostMaxGuestLimit(host); if (vmCount.longValue() == maxGuestLimit.longValue()){ if (s_logger.isDebugEnabled()) { s_logger.debug("Host name: " + host.getName() + ", hostId: "+ host.getId() +" already has max Running VMs(count includes system VMs), limit is: " + maxGuestLimit + " , skipping this and trying other available hosts"); } continue; } boolean numCpusGood = host.getCpus().intValue() >= offering.getCpu(); boolean cpuFreqGood = host.getSpeed().intValue() >= offering.getSpeed(); int cpu_requested = offering.getCpu() * offering.getSpeed(); long ram_requested = offering.getRamSize() * 1024L * 1024L; boolean hostHasCapacity = _capacityMgr.checkIfHostHasCapacity(host.getId(), cpu_requested, ram_requested, false, _factor, considerReservedCapacity); if (numCpusGood && cpuFreqGood && hostHasCapacity) { if (s_logger.isDebugEnabled()) { s_logger.debug("Found a suitable host, adding to list: " + host.getId()); } suitableHosts.add(host); } else { if (s_logger.isDebugEnabled()) { s_logger.debug("Not using host " + host.getId() + "; numCpusGood: " + numCpusGood + "; cpuFreqGood: " + cpuFreqGood + ", host has capacity?" + hostHasCapacity); } } } if (s_logger.isDebugEnabled()) { s_logger.debug("Host Allocator returning "+suitableHosts.size() +" suitable hosts"); } return suitableHosts; } private List<HostVO> reorderHostsByNumberOfVms(DeploymentPlan plan, List<HostVO> hosts, Account account) { if(account == null){ return hosts; } long dcId = plan.getDataCenterId(); Long podId = plan.getPodId(); Long clusterId = plan.getClusterId(); List<Long> hostIdsByVmCount = _vmInstanceDao.listHostIdsByVmCount(dcId, podId, clusterId, account.getAccountId()); if (s_logger.isDebugEnabled()) { s_logger.debug("List of hosts in ascending order of number of VMs: "+ hostIdsByVmCount); } //now filter the given list of Hosts by this ordered list Map<Long, HostVO> hostMap = new HashMap<Long, HostVO>(); for (HostVO host : hosts) { hostMap.put(host.getId(), host); } List<Long> matchingHostIds = new ArrayList<Long>(hostMap.keySet()); hostIdsByVmCount.retainAll(matchingHostIds); List<HostVO> reorderedHosts = new ArrayList<HostVO>(); for(Long id: hostIdsByVmCount){ reorderedHosts.add(hostMap.get(id)); } return reorderedHosts; } @Override public boolean isVirtualMachineUpgradable(VirtualMachine vm, ServiceOffering offering) { // currently we do no special checks to rule out a VM being upgradable to an offering, so // return true return true; } protected List<HostVO> prioritizeHosts(VMTemplateVO template, List<HostVO> hosts) { if (template == null) { return hosts; } // Determine the guest OS category of the template String templateGuestOSCategory = getTemplateGuestOSCategory(template); List<HostVO> prioritizedHosts = new ArrayList<HostVO>(); // If a template requires HVM and a host doesn't support HVM, remove it from consideration List<HostVO> hostsToCheck = new ArrayList<HostVO>(); 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<HostVO> highPriorityHosts = new ArrayList<HostVO>(); List<HostVO> lowPriorityHosts = new ArrayList<HostVO>(); 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(VMTemplateVO template) { long guestOSId = template.getGuestOSId(); GuestOSVO guestOS = _guestOSDao.findById(guestOSId); long guestOSCategoryId = guestOS.getCategoryId(); GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); return guestOSCategory.getName(); } protected Long getHostMaxGuestLimit(HostVO host) { HypervisorType hypervisorType = host.getHypervisorType(); String hypervisorVersion = host.getHypervisorVersion(); Long maxGuestLimit = _hypervisorCapabilitiesDao.getMaxGuestsLimit(hypervisorType, hypervisorVersion); return maxGuestLimit; } @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { _name = name; ComponentLocator locator = ComponentLocator.getCurrentLocator(); if (_configDao != null) { Map<String, String> configs = _configDao.getConfiguration(params); String opFactor = configs.get("cpu.overprovisioning.factor"); _factor = NumbersUtil.parseFloat(opFactor, 1); String allocationAlgorithm = configs.get("vm.allocation.algorithm"); if (allocationAlgorithm != null) { _allocationAlgorithm = allocationAlgorithm; } } return true; } @Override public String getName() { return _name; } @Override public boolean start() { return true; } @Override public boolean stop() { return true; } }