// 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.storage.allocator; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.offering.ServiceOffering; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.Inject; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.vm.DiskProfile; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; // // TODO // Rush to make LocalStoragePoolAllocator use static allocation status, we should revisit the overall // allocation process to make it more reliable in next release. The code put in here is pretty ugly // @Local(value = StoragePoolAllocator.class) public class LocalStoragePoolAllocator extends FirstFitStoragePoolAllocator { private static final Logger s_logger = Logger.getLogger(LocalStoragePoolAllocator.class); @Inject StoragePoolHostDao _poolHostDao; @Inject VMInstanceDao _vmInstanceDao; @Inject UserVmDao _vmDao; @Inject ServiceOfferingDao _offeringDao; @Inject CapacityDao _capacityDao; @Inject ConfigurationDao _configDao; protected GenericSearchBuilder<VMInstanceVO, Long> VmsOnPoolSearch; private int _secondsToSkipStoppedVMs = 86400; @Override public boolean allocatorIsCorrectType(DiskProfile dskCh) { return localStorageAllocationNeeded(dskCh); } @Override public List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { List<StoragePool> suitablePools = new ArrayList<StoragePool>(); // Check that the allocator type is correct if (!allocatorIsCorrectType(dskCh)) { return suitablePools; } ExcludeList myAvoids = new ExcludeList(avoid.getDataCentersToAvoid(), avoid.getPodsToAvoid(), avoid.getClustersToAvoid(), avoid.getHostsToAvoid(), avoid.getPoolsToAvoid()); if (s_logger.isDebugEnabled()) { s_logger.debug("LocalStoragePoolAllocator trying to find storage pool to fit the vm"); } List<StoragePool> availablePool; while (!(availablePool = super.allocateToPool(dskCh, vmProfile, plan, myAvoids, 1)).isEmpty()) { StoragePool pool = availablePool.get(0); myAvoids.addPool(pool.getId()); List<StoragePoolHostVO> hostsInSPool = _poolHostDao.listByPoolId(pool.getId()); assert (hostsInSPool.size() == 1) : "Local storage pool should be one host per pool"; s_logger.debug("Found suitable local storage pool " + pool.getId() + ", adding to list"); suitablePools.add(pool); if (suitablePools.size() == returnUpTo) { break; } } if (s_logger.isDebugEnabled()) { s_logger.debug("LocalStoragePoolAllocator returning " + suitablePools.size() + " suitable storage pools"); } if (suitablePools.isEmpty()) { if (s_logger.isDebugEnabled()) { s_logger.debug("Unable to find storage pool to fit the vm"); } } return suitablePools; } // we don't need to check host capacity now, since hostAllocators will do that anyway private boolean hostHasCpuMemoryCapacity(long hostId, List<Long> vmOnHost, VMInstanceVO vm) { ServiceOffering so = _offeringDao.findById(vm.getServiceOfferingId()); long usedMemory = calcHostAllocatedCpuMemoryCapacity(vmOnHost, CapacityVO.CAPACITY_TYPE_MEMORY); if (s_logger.isDebugEnabled()) { s_logger.debug("Calculated static-allocated memory for VMs on host " + hostId + ": " + usedMemory + " bytes, requesting memory: " + (so != null ? so.getRamSize() * 1024L * 1024L : "") + " bytes"); } SearchCriteria<CapacityVO> sc = _capacityDao.createSearchCriteria(); sc.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, hostId); sc.addAnd("capacityType", SearchCriteria.Op.EQ, CapacityVO.CAPACITY_TYPE_MEMORY); List<CapacityVO> capacities = _capacityDao.search(sc, null); if (capacities.size() > 0) { if (capacities.get(0).getTotalCapacity() < usedMemory + (so != null ? so.getRamSize() * 1024L * 1024L : 0)) { if (s_logger.isDebugEnabled()) { s_logger.debug("Host " + hostId + " runs out of memory capacity"); } return false; } } else { s_logger.warn("Host " + hostId + " has not reported memory capacity yet"); return false; } long usedCpu = calcHostAllocatedCpuMemoryCapacity(vmOnHost, CapacityVO.CAPACITY_TYPE_CPU); if (s_logger.isDebugEnabled()) { s_logger.debug("Calculated static-allocated CPU for VMs on host " + hostId + ": " + usedCpu + " GHz, requesting cpu: " + (so != null ? so.getCpu() * so.getSpeed() : "") + " GHz"); } sc = _capacityDao.createSearchCriteria(); sc.addAnd("hostOrPoolId", SearchCriteria.Op.EQ, hostId); sc.addAnd("capacityType", SearchCriteria.Op.EQ, CapacityVO.CAPACITY_TYPE_CPU); capacities = _capacityDao.search(sc, null); if (capacities.size() > 0) { if (capacities.get(0).getTotalCapacity() < usedCpu + (so != null ? so.getCpu() * so.getSpeed() : 0)) { if (s_logger.isDebugEnabled()) { s_logger.debug("Host " + hostId + " runs out of CPU capacity"); } return false; } } else { s_logger.warn("Host " + hostId + " has not reported CPU capacity yet"); return false; } return true; } private boolean skipCalculation(VMInstanceVO vm) { if (vm == null) { return true; } if (vm.getState() == State.Expunging) { if (s_logger.isDebugEnabled()) { s_logger.debug("Skip counting capacity for Expunging VM : " + vm.getInstanceName()); } return true; } if (vm.getState() == State.Destroyed && vm.getType() != VirtualMachine.Type.User) { return true; } if (vm.getState() == State.Stopped || vm.getState() == State.Destroyed) { // for stopped/Destroyed VMs, we will skip counting it if it hasn't been used for a while long millisecondsSinceLastUpdate = DateUtil.currentGMTTime().getTime() - vm.getUpdateTime().getTime(); if (millisecondsSinceLastUpdate > _secondsToSkipStoppedVMs * 1000L) { if (s_logger.isDebugEnabled()) { s_logger.debug("Skip counting vm " + vm.getInstanceName() + " in capacity allocation as it has been stopped for " + millisecondsSinceLastUpdate / 60000 + " minutes"); } return true; } } return false; } private long calcHostAllocatedCpuMemoryCapacity(List<Long> vmOnHost, short capacityType) { assert (capacityType == CapacityVO.CAPACITY_TYPE_MEMORY || capacityType == CapacityVO.CAPACITY_TYPE_CPU) : "Invalid capacity type passed in calcHostAllocatedCpuCapacity()"; long usedCapacity = 0; for (Long vmId : vmOnHost) { VMInstanceVO vm = _vmInstanceDao.findById(vmId); if (skipCalculation(vm)) { continue; } ServiceOffering so = _offeringDao.findById(vm.getServiceOfferingId()); if (vm.getType() == VirtualMachine.Type.User) { UserVmVO userVm = _vmDao.findById(vm.getId()); if (userVm == null) { continue; } } if (capacityType == CapacityVO.CAPACITY_TYPE_MEMORY) { usedCapacity += so.getRamSize() * 1024L * 1024L; } else if (capacityType == CapacityVO.CAPACITY_TYPE_CPU) { usedCapacity += so.getCpu() * so.getSpeed(); } } return usedCapacity; } @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { super.configure(name, params); _storageOverprovisioningFactor = new BigDecimal(1); _extraBytesPerVolume = NumbersUtil.parseLong((String) params.get("extra.bytes.per.volume"), 50 * 1024L * 1024L); Map<String, String> configs = _configDao.getConfiguration("management-server", params); String value = configs.get("vm.resource.release.interval"); _secondsToSkipStoppedVMs = NumbersUtil.parseInt(value, 86400); VmsOnPoolSearch = _vmInstanceDao.createSearchBuilder(Long.class); VmsOnPoolSearch.select(null, Func.DISTINCT, VmsOnPoolSearch.entity().getId()); VmsOnPoolSearch.and("removed", VmsOnPoolSearch.entity().getRemoved(), SearchCriteria.Op.NULL); VmsOnPoolSearch.and("state", VmsOnPoolSearch.entity().getState(), SearchCriteria.Op.NIN); SearchBuilder<VolumeVO> sbVolume = _volumeDao.createSearchBuilder(); sbVolume.and("poolId", sbVolume.entity().getPoolId(), SearchCriteria.Op.EQ); VmsOnPoolSearch.join("volumeJoin", sbVolume, VmsOnPoolSearch.entity().getId(), sbVolume.entity().getInstanceId(), JoinBuilder.JoinType.INNER); sbVolume.done(); VmsOnPoolSearch.done(); return true; } public LocalStoragePoolAllocator() { } }