package org.zstack.compute.allocator; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.transaction.annotation.Transactional; import org.zstack.core.db.DatabaseFacade; import org.zstack.header.allocator.AbstractHostAllocatorFlow; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.host.HostVO; import org.zstack.header.storage.primary.PrimaryStorageOverProvisioningManager; import org.zstack.header.storage.primary.PrimaryStorageState; import org.zstack.header.storage.primary.PrimaryStorageStatus; import org.zstack.header.vm.VmInstanceConstant.VmOperation; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import java.util.ArrayList; import java.util.List; @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) public class HostPrimaryStorageAllocatorFlow extends AbstractHostAllocatorFlow { @Autowired private DatabaseFacade dbf; @Autowired private PrimaryStorageOverProvisioningManager ratioMgr; @Transactional(readOnly = true) private List<HostVO> allocateFromCandidates() { List<String> huuids = getHostUuidsFromCandidates(); if (!VmOperation.NewCreate.toString().equals(spec.getVmOperation())) { String sql = "select h" + " from HostVO h" + " where h.uuid in :uuids" + " and h.clusterUuid in" + " (" + " select pr.clusterUuid" + " from PrimaryStorageClusterRefVO pr, PrimaryStorageVO pri, PrimaryStorageCapacityVO cap" + " where pr.primaryStorageUuid = pri.uuid" + " and pri.uuid = cap.uuid" + " and (pri.state = :state or pri.state =:state1)" + " and pri.status = :status" + " )"; TypedQuery<HostVO> query = dbf.getEntityManager().createQuery(sql, HostVO.class); query.setParameter("uuids", huuids); query.setParameter("state", PrimaryStorageState.Enabled); query.setParameter("state1", PrimaryStorageState.Disabled); query.setParameter("status", PrimaryStorageStatus.Connected); return query.getResultList(); } // for new created vm String sql = "select ps.uuid, cap.availableCapacity" + " from PrimaryStorageClusterRefVO ref, PrimaryStorageVO ps, HostVO h, PrimaryStorageCapacityVO cap" + " where ref.primaryStorageUuid = ps.uuid" + " and cap.uuid = ps.uuid" + " and ps.state = :state" + " and ps.status = :status" + " and ref.clusterUuid = h.clusterUuid" + " and h.uuid in (:huuids)"; TypedQuery<Tuple> q = dbf.getEntityManager().createQuery(sql, Tuple.class); q.setParameter("state", PrimaryStorageState.Enabled); q.setParameter("status", PrimaryStorageStatus.Connected); q.setParameter("huuids", huuids); List<Tuple> ts = q.getResultList(); if (ts.isEmpty()) { return new ArrayList<>(); } List<String> psUuids = new ArrayList<>(); for (Tuple t : ts) { psUuids.add(t.get(0, String.class)); } if (spec.getRequiredPrimaryStorageUuid() != null) { if (psUuids.contains(spec.getRequiredPrimaryStorageUuid())) { psUuids.clear(); psUuids.add(0, spec.getRequiredPrimaryStorageUuid()); } else { return new ArrayList<>(); } } sql = "select i.primaryStorageUuid from ImageCacheVO i where i.primaryStorageUuid in (:psUuids) and i.imageUuid = :iuuid"; TypedQuery<String> iq = dbf.getEntityManager().createQuery(sql, String.class); iq.setParameter("psUuids", psUuids); iq.setParameter("iuuid", spec.getImage().getUuid()); List<String> hasImagePrimaryStorage = iq.getResultList(); List<String> psCandidates = new ArrayList<>(); for (Tuple t : ts) { String psUuid = t.get(0, String.class); long cap = t.get(1, Long.class); if (hasImagePrimaryStorage.contains(psUuid)) { cap = ratioMgr.calculatePrimaryStorageAvailableCapacityByRatio(psUuid, cap); } else { // the primary storage doesn't have the image in cache // so we need to add the image size cap = ratioMgr.calculatePrimaryStorageAvailableCapacityByRatio(psUuid, cap) + spec.getImage().getActualSize(); } if (cap > spec.getDiskSize()) { psCandidates.add(psUuid); } } if (psCandidates.isEmpty()) { return new ArrayList<>(); } sql = "select h" + " from HostVO h, PrimaryStorageClusterRefVO ref" + " where ref.clusterUuid = h.clusterUuid" + " and ref.primaryStorageUuid in (:psUuids)" + " and h.uuid in (:huuids)"; TypedQuery<HostVO> hq = dbf.getEntityManager().createQuery(sql, HostVO.class); hq.setParameter("psUuids", psCandidates); hq.setParameter("huuids", huuids); return hq.getResultList(); } @Override public void allocate() { if (amITheFirstFlow()) { throw new CloudRuntimeException("HostPrimaryStorageAllocatorFlow cannot be the first flow in the chain"); } candidates = allocateFromCandidates(); if (candidates.isEmpty()) { String err = spec.getVmOperation().equals(VmOperation.NewCreate.toString()) ? String.format("cannot find available primary storage[state: %s, status: %s, available capacity %s bytes]." + " Check the state/status of primary storage and make sure they have been attached to clusters", PrimaryStorageState.Enabled, PrimaryStorageStatus.Connected, spec.getDiskSize()) : String.format("cannot find available primary storage[state: %s, status: %s]." + " Check the state/status of primary storage and make sure they have been attached to clusters", PrimaryStorageState.Enabled, PrimaryStorageStatus.Connected); fail(err); } else { next(candidates); } } }