package org.zstack.compute.allocator; import org.springframework.beans.factory.annotation.Autowired; import org.zstack.compute.cluster.ClusterSystemTags; import org.zstack.compute.host.HostSystemTags; import org.zstack.compute.zone.ZoneSystemTags; import org.zstack.core.componentloader.PluginRegistry; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.header.Component; import org.zstack.header.allocator.HostCapacityOverProvisioningManager; import org.zstack.header.allocator.HostReservedCapacityExtensionPoint; import org.zstack.header.allocator.ReservedHostCapacity; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.host.HostState; import org.zstack.header.host.HostStatus; import org.zstack.header.host.HostVO; import org.zstack.header.host.HostVO_; import org.zstack.utils.CollectionUtils; import org.zstack.utils.SizeUtils; import org.zstack.utils.Utils; import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import javax.persistence.Tuple; import java.util.*; import java.util.Map.Entry; /** */ public class HostCapacityReserveManagerImpl implements HostCapacityReserveManager, Component { private static final CLogger logger = Utils.getLogger(HostCapacityReserveManagerImpl.class); @Autowired private DatabaseFacade dbf; @Autowired private PluginRegistry pluginRgty; @Autowired private HostCapacityOverProvisioningManager ratioMgr; private Map<String, HostReservedCapacityExtensionPoint> exts = new HashMap<>(); private void populateExtensions() { for (HostReservedCapacityExtensionPoint extp : pluginRgty.getExtensionList(HostReservedCapacityExtensionPoint.class)) { HostReservedCapacityExtensionPoint ext = exts.get(extp.getHypervisorTypeForHostReserveCapacityExtension()); if (ext != null) { throw new CloudRuntimeException(String.format("duplicate HostReserveCapacityExtensionPoint[%s, %s] for hypervisor type[%s]", extp.getClass().getName(), ext.getClass().getName(), extp.getHypervisorTypeForHostReserveCapacityExtension())); } exts.put(extp.getHypervisorTypeForHostReserveCapacityExtension(), extp); } } @Override public boolean start() { populateExtensions(); return true; } @Override public boolean stop() { return true; } private class ReservedCapacityFinder { List<String> hostUuids; Map<String, ReservedHostCapacity> result = new HashMap<>(); private void findReservedCapacityByHostTag() { if (!HostAllocatorGlobalConfig.HOST_LEVEL_RESERVE_CAPACITY.value(Boolean.class)) { return; } Map<String, List<String>> cpuTags = HostSystemTags.RESERVED_CPU_CAPACITY.getTags(hostUuids); for (Entry<String, List<String>> e : cpuTags.entrySet()) { ReservedHostCapacity hc = result.get(e.getKey()); String capacityString = HostSystemTags.RESERVED_CPU_CAPACITY.getTokenByTag(e.getValue().get(0), "capacity"); hc.setReservedCpuCapacity(SizeUtils.sizeStringToBytes(capacityString)); } Map<String, List<String>> memTags = HostSystemTags.RESERVED_MEMORY_CAPACITY.getTags(hostUuids); for (Entry<String, List<String>> e : memTags.entrySet()) { ReservedHostCapacity hc = result.get(e.getKey()); String capacityString = HostSystemTags.RESERVED_MEMORY_CAPACITY.getTokenByTag(e.getValue().get(0), "capacity"); hc.setReservedMemoryCapacity(SizeUtils.sizeStringToBytes(capacityString)); } } private void findReservedCapacityByClusterTag() { if (!HostAllocatorGlobalConfig.CLUSTER_LEVEL_RESERVE_CAPACITY.value(Boolean.class)) { return; } SimpleQuery<HostVO> clusterq = dbf.createQuery(HostVO.class); clusterq.select(HostVO_.uuid, HostVO_.clusterUuid); clusterq.add(HostVO_.uuid, Op.IN, hostUuids); clusterq.add(HostVO_.state,Op.EQ, HostState.Enabled); clusterq.add(HostVO_.status,Op.EQ, HostStatus.Connected); List<Tuple> clusterTuple = clusterq.listTuple(); Map<String, List<String>> clusterHostUuidMap = new HashMap<>(clusterTuple.size()); List<String> clusterUuids = new ArrayList<>(clusterTuple.size()); for (Tuple t : clusterTuple) { String huuid = t.get(0, String.class); String cuuid = t.get(1, String.class); List<String> huuids = clusterHostUuidMap.get(cuuid); if (huuids == null) { huuids = new ArrayList<>(); clusterHostUuidMap.put(cuuid, huuids); } huuids.add(huuid); clusterUuids.add(cuuid); } Map<String, List<String>> cpuTags = ClusterSystemTags.HOST_RESERVED_CPU_CAPACITY.getTags(clusterUuids); for (Entry<String, List<String>> e : cpuTags.entrySet()) { List<String> huuids = clusterHostUuidMap.get(e.getKey()); for (String huuid : huuids) { ReservedHostCapacity hc = result.get(huuid); if (hc.getReservedCpuCapacity() != -1) { continue; } String capacityString = ClusterSystemTags.HOST_RESERVED_CPU_CAPACITY.getTokenByTag(e.getValue().get(0), "capacity"); hc.setReservedCpuCapacity(SizeUtils.sizeStringToBytes(capacityString)); } } Map<String, List<String>> memTags = ClusterSystemTags.HOST_RESERVED_MEMORY_CAPACITY.getTags(clusterUuids); for (Entry<String, List<String>> e : memTags.entrySet()) { List<String> huuids = clusterHostUuidMap.get(e.getKey()); for (String huuid : huuids) { ReservedHostCapacity hc = result.get(huuid); if (hc.getReservedMemoryCapacity() != -1) { continue; } String capacityString = ClusterSystemTags.HOST_RESERVED_MEMORY_CAPACITY.getTokenByTag(e.getValue().get(0), "capacity"); hc.setReservedMemoryCapacity(SizeUtils.sizeStringToBytes(capacityString)); } } } private void findReservedCapacityByZoneTag() { if (!HostAllocatorGlobalConfig.ZONE_LEVEL_RESERVE_CAPACITY.value(Boolean.class)) { return; } SimpleQuery<HostVO> zoneq = dbf.createQuery(HostVO.class); zoneq.select(HostVO_.uuid, HostVO_.zoneUuid); zoneq.add(HostVO_.uuid, Op.IN, hostUuids); zoneq.add(HostVO_.state,Op.EQ, HostState.Enabled); zoneq.add(HostVO_.status,Op.EQ, HostStatus.Connected); List<Tuple> zoneTuples = zoneq.listTuple(); List<String> zoneUuids = new ArrayList<>(); Map<String, List<String>> zoneHostUuidMap = new HashMap<>(); for (Tuple t : zoneTuples) { String huuid = t.get(0, String.class); String zuuid = t.get(1, String.class); List<String> huuids = zoneHostUuidMap.get(zuuid); if (huuids == null) { huuids = new ArrayList<>(); zoneHostUuidMap.put(zuuid, huuids); } huuids.add(huuid); zoneUuids.add(zuuid); } Map<String, List<String>> ctags = ZoneSystemTags.HOST_RESERVED_CPU_CAPACITY.getTags(zoneUuids); for (Entry<String, List<String>> e : ctags.entrySet()) { List<String> huuids = zoneHostUuidMap.get(e.getKey()); for (String huuid : huuids) { ReservedHostCapacity hc = result.get(huuid); if (hc.getReservedCpuCapacity() != -1) { continue; } String capacityString = ZoneSystemTags.HOST_RESERVED_CPU_CAPACITY.getTokenByTag(e.getValue().get(0), "capacity"); hc.setReservedCpuCapacity(SizeUtils.sizeStringToBytes(capacityString)); } } Map<String, List<String>> memTags = ZoneSystemTags.HOST_RESERVED_MEMORY_CAPACITY.getTags(zoneUuids); for (Entry<String, List<String>> e : memTags.entrySet()) { List<String> huuids = zoneHostUuidMap.get(e.getKey()); for (String huuid : huuids) { ReservedHostCapacity hc = result.get(huuid); if (hc.getReservedMemoryCapacity() != -1) { continue; } String capacityString = ZoneSystemTags.HOST_RESERVED_MEMORY_CAPACITY.getTokenByTag(e.getValue().get(0), "capacity"); hc.setReservedMemoryCapacity(SizeUtils.sizeStringToBytes(capacityString)); } } } private void findReservedCapacityByHypervisorType() { SimpleQuery<HostVO> hq = dbf.createQuery(HostVO.class); hq.select(HostVO_.uuid, HostVO_.hypervisorType); hq.add(HostVO_.uuid, Op.IN, hostUuids); hq.add(HostVO_.state,Op.EQ, HostState.Enabled); hq.add(HostVO_.status,Op.EQ, HostStatus.Connected); List<Tuple> tuples = hq.listTuple(); for (Tuple t : tuples) { String huuid = t.get(0, String.class); String hvType = t.get(1, String.class); HostReservedCapacityExtensionPoint ext = exts.get(hvType); if (ext == null) { continue; } ReservedHostCapacity hc = result.get(huuid); if (hc.getReservedMemoryCapacity() == -1) { hc.setReservedMemoryCapacity(ext.getReservedHostCapacity().getReservedMemoryCapacity()); } if (hc.getReservedCpuCapacity() == -1) { hc.setReservedCpuCapacity(ext.getReservedHostCapacity().getReservedCpuCapacity()); } } } private void squeeze() { result.entrySet() .stream() .filter(e -> e.getValue().getReservedCpuCapacity() != -1 && e.getValue().getReservedMemoryCapacity() != -1) .forEach(e -> hostUuids.remove(e.getKey())); } private void done() { for (ReservedHostCapacity hc : result.values()) { if (hc.getReservedCpuCapacity() == -1) { hc.setReservedCpuCapacity(0); } if (hc.getReservedMemoryCapacity() == -1) { hc.setReservedMemoryCapacity(0); } } } Map<String, ReservedHostCapacity> find() { if (hostUuids.isEmpty()) { return result; } for (String huuid : hostUuids) { ReservedHostCapacity hc = new ReservedHostCapacity(); hc.setReservedCpuCapacity(-1); hc.setReservedMemoryCapacity(-1); result.put(huuid, hc); } findReservedCapacityByHostTag(); squeeze(); if (hostUuids.isEmpty()) { return result; } findReservedCapacityByClusterTag(); squeeze(); if (hostUuids.isEmpty()) { return result; } findReservedCapacityByZoneTag(); squeeze(); if (hostUuids.isEmpty()) { return result; } findReservedCapacityByHypervisorType(); done(); return result; } } @Override public List<HostVO> filterOutHostsByReservedCapacity(List<HostVO> candidates, long requiredCpu, long requiredMemory) { ReservedCapacityFinder finder = new ReservedCapacityFinder(); finder.hostUuids = CollectionUtils.transformToList(candidates, new Function<String, HostVO>() { @Override public String call(HostVO arg) { return arg.getUuid(); } }); Map<String, ReservedHostCapacity> reserves = finder.find(); List<HostVO> ret = new ArrayList<>(candidates.size()); for (HostVO hvo : candidates) { ReservedHostCapacity hc = reserves.get(hvo.getUuid()); if (hvo.getCapacity().getAvailableMemory() - hc.getReservedMemoryCapacity() >= ratioMgr.calculateMemoryByRatio(hvo.getUuid(), requiredMemory)) { ret.add(hvo); } else { if (logger.isTraceEnabled()) { if (hvo.getCapacity().getAvailableMemory() - hc.getReservedMemoryCapacity() < requiredMemory) { logger.trace(String.format("remove host[uuid:%s] from candidates;because after subtracting reserved memory[%s bytes]," + " it cannot provide required memory[%s bytes]", hvo.getUuid(), hc.getReservedMemoryCapacity(), requiredMemory)); } if (hvo.getCapacity().getAvailableCpu() - hc.getReservedCpuCapacity() < requiredCpu) { logger.trace(String.format("remove host[uuid:%s] from candidates;because after subtracting reserved cpu[%s]," + " it cannot provide required cpu[%s]", hvo.getUuid(), hc.getReservedCpuCapacity(), requiredCpu)); } } } } return ret; } @Override public ReservedHostCapacity getReservedHostCapacityByZones(List<String> zoneUuids) { ReservedHostCapacity ret = new ReservedHostCapacity(); ret.setReservedCpuCapacity(0); ret.setReservedMemoryCapacity(0); SimpleQuery<HostVO> q = dbf.createQuery(HostVO.class); q.select(HostVO_.uuid); q.add(HostVO_.zoneUuid, Op.IN, zoneUuids); List<String> huuids = q.listValue(); if (huuids.isEmpty()) { return ret; } ReservedCapacityFinder finder = new ReservedCapacityFinder(); finder.hostUuids = huuids; Collection<ReservedHostCapacity> col = finder.find().values(); for (ReservedHostCapacity rc : col) { ret.setReservedMemoryCapacity(ret.getReservedMemoryCapacity() + rc.getReservedMemoryCapacity()); ret.setReservedCpuCapacity(ret.getReservedCpuCapacity() + rc.getReservedCpuCapacity()); } return ret; } @Override public ReservedHostCapacity getReservedHostCapacityByClusters(List<String> clusterUuids) { ReservedHostCapacity ret = new ReservedHostCapacity(); ret.setReservedCpuCapacity(0); ret.setReservedMemoryCapacity(0); SimpleQuery<HostVO> q = dbf.createQuery(HostVO.class); q.select(HostVO_.uuid); q.add(HostVO_.clusterUuid, Op.IN, clusterUuids); List<String> huuids = q.listValue(); if (huuids.isEmpty()) { return ret; } ReservedCapacityFinder finder = new ReservedCapacityFinder(); finder.hostUuids = huuids; Collection<ReservedHostCapacity> col = finder.find().values(); for (ReservedHostCapacity rc : col) { ret.setReservedMemoryCapacity(ret.getReservedMemoryCapacity() + rc.getReservedMemoryCapacity()); ret.setReservedCpuCapacity(ret.getReservedCpuCapacity() + rc.getReservedCpuCapacity()); } return ret; } @Override public ReservedHostCapacity getReservedHostCapacityByHosts(List<String> hostUuids) { ReservedCapacityFinder finder = new ReservedCapacityFinder(); finder.hostUuids = hostUuids; return finder.find().values().iterator().next(); } }