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.host.HostVO; import org.zstack.utils.Utils; import org.zstack.utils.logging.CLogger; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import java.util.*; /** * This flow returns a list of host candidates sorted by the number of their VMs. */ @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE) public class LeastVmPreferredAllocatorFlow extends AbstractHostAllocatorFlow { private static final CLogger logger = Utils.getLogger(LeastVmPreferredAllocatorFlow.class); @Autowired private DatabaseFacade dbf; @Transactional(readOnly = true) private List<Tuple> findLeastVmHost(List<String> huuids) { String sql = "select count(vm) as cnt, host.uuid" + " from HostVO host" + " Left Join VmInstanceVO vm on host.uuid = vm.hostUuid" + " where host.uuid in (:huuids)" + " group by host.uuid order by cnt"; TypedQuery<Tuple> q = dbf.getEntityManager().createQuery(sql, Tuple.class); q.setParameter("huuids", huuids); return q.getResultList(); } @Override public void allocate() { throwExceptionIfIAmTheFirstFlow(); if (spec.isListAllHosts()) { next(candidates); return; } List<String> huuids = getHostUuidsFromCandidates(); List<Tuple> tuples = findLeastVmHost(huuids); // no VM running on any candidate host(s) if (tuples.isEmpty()) { next(candidates); return; } List<HostVO> sorted = new ArrayList<>(candidates.size()); Map<String, HostVO> dict = new HashMap<>(); for (HostVO hvo: candidates) { dict.put(hvo.getUuid(), hvo); } if (huuids.size() > tuples.size()) { HashSet<String> hostsWithVMs = new HashSet<>(); for (Tuple t : tuples) { String hostUuid = t.get(1, String.class); hostsWithVMs.add(hostUuid); } for (String huuid : huuids) { if (!hostsWithVMs.contains(huuid)) { sorted.add(dict.get(huuid)); } } } // Note: the query result is ordered. for (Tuple t : tuples) { String hostUuid = t.get(1, String.class); sorted.add(dict.get(hostUuid)); } next(sorted); } }