// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with 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. package org.apache.cloudstack.storage.allocator; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.storage.Storage; import org.apache.log4j.Logger; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import com.cloud.capacity.Capacity; import com.cloud.capacity.dao.CapacityDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.Volume; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.Account; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.AdapterBase; import com.cloud.vm.DiskProfile; import com.cloud.vm.VirtualMachineProfile; public abstract class AbstractStoragePoolAllocator extends AdapterBase implements StoragePoolAllocator { private static final Logger s_logger = Logger.getLogger(AbstractStoragePoolAllocator.class); @Inject StorageManager storageMgr; protected @Inject PrimaryDataStoreDao _storagePoolDao; @Inject VolumeDao _volumeDao; @Inject ConfigurationDao _configDao; @Inject ClusterDao _clusterDao; protected @Inject DataStoreManager dataStoreMgr; protected BigDecimal _storageOverprovisioningFactor = new BigDecimal(1); long _extraBytesPerVolume = 0; Random _rand; boolean _dontMatter; protected String _allocationAlgorithm = "random"; @Inject DiskOfferingDao _diskOfferingDao; @Inject CapacityDao _capacityDao; @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { super.configure(name, params); if(_configDao != null) { Map<String, String> configs = _configDao.getConfiguration(null, params); String globalStorageOverprovisioningFactor = configs.get("storage.overprovisioning.factor"); _storageOverprovisioningFactor = new BigDecimal(NumbersUtil.parseFloat(globalStorageOverprovisioningFactor, 2.0f)); _extraBytesPerVolume = 0; _rand = new Random(System.currentTimeMillis()); _dontMatter = Boolean.parseBoolean(configs.get("storage.overwrite.provisioning")); String allocationAlgorithm = configs.get("vm.allocation.algorithm"); if (allocationAlgorithm != null) { _allocationAlgorithm = allocationAlgorithm; } return true; } return false; } protected abstract List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo); @Override public List<StoragePool> allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { List<StoragePool> pools = select(dskCh, vmProfile, plan, avoid, returnUpTo); return reOrder(pools, vmProfile, plan); } protected List<StoragePool> reorderPoolsByCapacity(DeploymentPlan plan, List<StoragePool> pools) { Long clusterId = plan.getClusterId(); short capacityType; if(pools != null && pools.size() != 0){ capacityType = pools.get(0).getPoolType().isShared() == true ? Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED : Capacity.CAPACITY_TYPE_LOCAL_STORAGE; } else{ return null; } List<Long> poolIdsByCapacity = _capacityDao.orderHostsByFreeCapacity(clusterId, capacityType); if (s_logger.isDebugEnabled()) { s_logger.debug("List of pools in descending order of free capacity: "+ poolIdsByCapacity); } //now filter the given list of Pools by this ordered list Map<Long, StoragePool> poolMap = new HashMap<Long, StoragePool>(); for (StoragePool pool : pools) { poolMap.put(pool.getId(), pool); } List<Long> matchingPoolIds = new ArrayList<Long>(poolMap.keySet()); poolIdsByCapacity.retainAll(matchingPoolIds); List<StoragePool> reorderedPools = new ArrayList<StoragePool>(); for(Long id: poolIdsByCapacity){ reorderedPools.add(poolMap.get(id)); } return reorderedPools; } protected List<StoragePool> reorderPoolsByNumberOfVolumes(DeploymentPlan plan, List<StoragePool> pools, Account account) { if (account == null) { return pools; } long dcId = plan.getDataCenterId(); Long podId = plan.getPodId(); Long clusterId = plan.getClusterId(); List<Long> poolIdsByVolCount = _volumeDao.listPoolIdsByVolumeCount(dcId, podId, clusterId, account.getAccountId()); if (s_logger.isDebugEnabled()) { s_logger.debug("List of pools in ascending order of number of volumes for account id: " + account.getAccountId() + " is: " + poolIdsByVolCount); } // now filter the given list of Pools by this ordered list Map<Long, StoragePool> poolMap = new HashMap<Long, StoragePool>(); for (StoragePool pool : pools) { poolMap.put(pool.getId(), pool); } List<Long> matchingPoolIds = new ArrayList<Long>(poolMap.keySet()); poolIdsByVolCount.retainAll(matchingPoolIds); List<StoragePool> reorderedPools = new ArrayList<StoragePool>(); for (Long id : poolIdsByVolCount) { reorderedPools.add(poolMap.get(id)); } return reorderedPools; } protected List<StoragePool> reOrder(List<StoragePool> pools, VirtualMachineProfile vmProfile, DeploymentPlan plan) { if (pools == null) { return null; } Account account = null; if (vmProfile.getVirtualMachine() != null) { account = vmProfile.getOwner(); } if (_allocationAlgorithm.equals("random") || _allocationAlgorithm.equals("userconcentratedpod_random") || (account == null)) { // Shuffle this so that we don't check the pools in the same order. Collections.shuffle(pools); } else if (_allocationAlgorithm.equals("userdispersing")) { pools = reorderPoolsByNumberOfVolumes(plan, pools, account); } else if(_allocationAlgorithm.equals("firstfitleastconsumed")){ pools = reorderPoolsByCapacity(plan, pools); } return pools; } protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh, DeploymentPlan plan) { if (s_logger.isDebugEnabled()) { s_logger.debug("Checking if storage pool is suitable, name: " + pool.getName() + " ,poolId: " + pool.getId()); } if (avoid.shouldAvoid(pool)) { if (s_logger.isDebugEnabled()) { s_logger.debug("StoragePool is in avoid set, skipping this pool"); } return false; } Long clusterId = pool.getClusterId(); if (clusterId != null) { ClusterVO cluster = _clusterDao.findById(clusterId); if (!(cluster.getHypervisorType() == dskCh.getHypervisorType())) { if (s_logger.isDebugEnabled()) { s_logger.debug("StoragePool's Cluster does not have required hypervisorType, skipping this pool"); } return false; } } else if (pool.getHypervisor() != null && !pool.getHypervisor().equals(HypervisorType.Any) && !(pool.getHypervisor() == dskCh.getHypervisorType())) { if (s_logger.isDebugEnabled()) { s_logger.debug("StoragePool does not have required hypervisorType, skipping this pool"); } return false; } if(!checkHypervisorCompatibility(dskCh.getHypervisorType(), dskCh.getType(), pool.getPoolType())){ return false; } // check capacity Volume volume = _volumeDao.findById(dskCh.getVolumeId()); List<Volume> requestVolumes = new ArrayList<Volume>(); requestVolumes.add(volume); return storageMgr.storagePoolHasEnoughIops(requestVolumes, pool) && storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool, plan.getClusterId()); } /* Check StoragePool and Volume type compatibility for the hypervisor */ private boolean checkHypervisorCompatibility(HypervisorType hyperType, Volume.Type volType, Storage.StoragePoolType poolType){ if(HypervisorType.LXC.equals(hyperType)){ if(Volume.Type.ROOT.equals(volType)){ //LXC ROOT disks supports NFS and local storage pools only if(!(Storage.StoragePoolType.NetworkFilesystem.equals(poolType) || Storage.StoragePoolType.Filesystem.equals(poolType)) ){ s_logger.debug("StoragePool does not support LXC ROOT disk, skipping this pool"); return false; } } else if (Volume.Type.DATADISK.equals(volType)){ //LXC DATA disks supports RBD storage pool only if(!Storage.StoragePoolType.RBD.equals(poolType)){ s_logger.debug("StoragePool does not support LXC DATA disk, skipping this pool"); return false; } } } return true; } }