package org.zstack.storage.primary.local; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.Q; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.errorcode.ErrorFacade; import org.zstack.core.workflow.FlowChainBuilder; import org.zstack.header.Component; import org.zstack.header.allocator.HostAllocatorError; import org.zstack.header.allocator.HostAllocatorFilterExtensionPoint; import org.zstack.header.allocator.HostAllocatorSpec; import org.zstack.header.allocator.HostAllocatorStrategyExtensionPoint; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.host.HostVO; import org.zstack.header.storage.primary.*; import org.zstack.header.vm.VmInstanceConstant.VmOperation; import org.zstack.utils.CollectionUtils; import org.zstack.utils.Utils; import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; /** * Created by frank on 7/1/2015. */ public class LocalStorageAllocatorFactory implements PrimaryStorageAllocatorStrategyFactory, Component, HostAllocatorFilterExtensionPoint, PrimaryStorageAllocatorStrategyExtensionPoint, PrimaryStorageAllocatorFlowNameSetter, HostAllocatorStrategyExtensionPoint { private CLogger logger = Utils.getLogger(LocalStorageAllocatorFactory.class); @Autowired private DatabaseFacade dbf; @Autowired private ErrorFacade errf; @Autowired private PrimaryStorageOverProvisioningManager ratioMgr; public static PrimaryStorageAllocatorStrategyType type = new PrimaryStorageAllocatorStrategyType(LocalStorageConstants.LOCAL_STORAGE_ALLOCATOR_STRATEGY); private List<String> allocatorFlowNames; private FlowChainBuilder builder = new FlowChainBuilder(); private LocalStorageAllocatorStrategy strategy; @Override public PrimaryStorageAllocatorStrategyType getPrimaryStorageAllocatorStrategyType() { return type; } @Override public PrimaryStorageAllocatorStrategy getPrimaryStorageAllocatorStrategy() { return strategy; } public List<String> getAllocatorFlowNames() { return allocatorFlowNames; } public void setAllocatorFlowNames(List<String> allocatorFlowNames) { this.allocatorFlowNames = allocatorFlowNames; } @Override public boolean start() { builder.setFlowClassNames(allocatorFlowNames).construct(); strategy = new LocalStorageAllocatorStrategy(builder); return true; } @Override public boolean stop() { return true; } @Override public List<HostVO> filterHostCandidates(List<HostVO> candidates, HostAllocatorSpec spec) { if (VmOperation.NewCreate.toString().equals(spec.getVmOperation())) { List<String> huuids = CollectionUtils.transformToList(candidates, new Function<String, HostVO>() { @Override public String call(HostVO arg) { return arg.getUuid(); } }); SimpleQuery<LocalStorageHostRefVO> q = dbf.createQuery(LocalStorageHostRefVO.class); q.select(LocalStorageHostRefVO_.hostUuid, LocalStorageHostRefVO_.availableCapacity, LocalStorageResourceRefVO_.primaryStorageUuid); q.add(LocalStorageHostRefVO_.hostUuid, Op.IN, huuids); List<Tuple> ts = q.listTuple(); final Set<String> toRemoveHuuids = new HashSet<>(); for (Tuple t : ts) { String huuid = t.get(0, String.class); long cap = t.get(1, Long.class); String psUuid = t.get(2, String.class); if (cap < ratioMgr.calculateByRatio(psUuid, spec.getDiskSize())) { toRemoveHuuids.add(huuid); } } // for more than one local storage, maybe one of it fit the requirement for (Tuple t : ts) { String huuid = t.get(0, String.class); long cap = t.get(1, Long.class); String psUuid = t.get(2, String.class); if (cap >= ratioMgr.calculateByRatio(psUuid, spec.getDiskSize())) { toRemoveHuuids.remove(huuid); } } if (!toRemoveHuuids.isEmpty()) { logger.debug(String.format("local storage filters out hosts%s, because they don't have required disk capacity[%s bytes]", toRemoveHuuids, spec.getDiskSize())); candidates = CollectionUtils.transformToList(candidates, new Function<HostVO, HostVO>() { @Override public HostVO call(HostVO arg) { return toRemoveHuuids.contains(arg.getUuid()) ? null : arg; } }); if (candidates.isEmpty()) { throw new OperationFailureException(errf.instantiateErrorCode(HostAllocatorError.NO_AVAILABLE_HOST, String.format("the local primary storage has no hosts with enough disk capacity[%s bytes] required by the vm[uuid:%s]", spec.getDiskSize(), spec.getVmInstance().getUuid()) )); } } } else if (VmOperation.Start.toString().equals(spec.getVmOperation())) { final LocalStorageResourceRefVO ref = Q.New(LocalStorageResourceRefVO.class) .eq(LocalStorageResourceRefVO_.resourceUuid, spec.getVmInstance().getRootVolumeUuid()) .find(); if (ref != null) { candidates = CollectionUtils.transformToList(candidates, new Function<HostVO, HostVO>() { @Override public HostVO call(HostVO arg) { return arg.getUuid().equals(ref.getHostUuid()) ? arg : null; } }); if (candidates.isEmpty()) { throw new OperationFailureException(errf.instantiateErrorCode(HostAllocatorError.NO_AVAILABLE_HOST, String.format("the vm[uuid: %s] using local primary storage can only be started on the host[uuid: %s], but the host is either not having enough CPU/memory or in" + " the state[Enabled] or status[Connected] to start the vm", spec.getVmInstance().getUuid(), ref.getHostUuid()) )); } } } /* else if (VmOperation.Migrate.toString().equals(spec.getVmOperation())) { final LocalStorageResourceRefVO ref = dbf.findByUuid(spec.getVmInstance().getRootVolumeUuid(), LocalStorageResourceRefVO.class); if (ref != null) { throw new OperationFailureException(errf.instantiateErrorCode(HostAllocatorError.NO_AVAILABLE_HOST, String.format("the vm[uuid: %s] cannot migrate because of using local primary storage on the host[uuid: %s]", spec.getVmInstance().getUuid(), ref.getHostUuid()) )); } } */ return candidates; } @Override public String getPrimaryStorageAllocatorStrategyName(final AllocatePrimaryStorageMsg msg) { String allocatorType = null; if (msg.getExcludeAllocatorStrategies() != null && msg.getExcludeAllocatorStrategies().contains(LocalStorageConstants.LOCAL_STORAGE_ALLOCATOR_STRATEGY) ) { allocatorType = null; } else if (LocalStorageConstants.LOCAL_STORAGE_ALLOCATOR_STRATEGY.equals(msg.getAllocationStrategy())) { allocatorType = LocalStorageConstants.LOCAL_STORAGE_ALLOCATOR_STRATEGY; } else if (msg.getRequiredPrimaryStorageUuid() != null) { SimpleQuery<PrimaryStorageVO> q = dbf.createQuery(PrimaryStorageVO.class); q.select(PrimaryStorageVO_.type); q.add(PrimaryStorageVO_.uuid, Op.EQ, msg.getRequiredPrimaryStorageUuid()); String type = q.findValue(); if (LocalStorageConstants.LOCAL_STORAGE_TYPE.equals(type)) { allocatorType = LocalStorageConstants.LOCAL_STORAGE_ALLOCATOR_STRATEGY; } } else if (msg.getRequiredHostUuid() != null) { allocatorType = new Callable<String>() { @Override @Transactional(readOnly = true) public String call() { String sql = "select ps.type" + " from PrimaryStorageVO ps, PrimaryStorageClusterRefVO ref, HostVO host" + " where ps.uuid = ref.primaryStorageUuid" + " and ref.clusterUuid = host.clusterUuid" + " and host.uuid = :huuid"; TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class); q.setParameter("huuid", msg.getRequiredHostUuid()); List<String> types = q.getResultList(); for (String type : types) { if (type.equals(LocalStorageConstants.LOCAL_STORAGE_TYPE)) { return LocalStorageConstants.LOCAL_STORAGE_ALLOCATOR_STRATEGY; } } return null; } }.call(); } return allocatorType; } @Override public String getHostAllocatorStrategyName(HostAllocatorSpec spec) { if (!VmOperation.Migrate.toString().equals(spec.getVmOperation())) { return null; } SimpleQuery<PrimaryStorageVO> q = dbf.createQuery(PrimaryStorageVO.class); q.select(PrimaryStorageVO_.type); q.add(PrimaryStorageVO_.uuid, Op.EQ, spec.getVmInstance().getRootVolume().getPrimaryStorageUuid()); String type = q.findValue(); if (!LocalStorageConstants.LOCAL_STORAGE_TYPE.equals(type)) { return null; } return LocalStorageConstants.LOCAL_STORAGE_MIGRATE_VM_ALLOCATOR_TYPE; } }