package org.ovirt.engine.core.bll; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.ovirt.engine.core.common.action.MigrateVmToServerParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.businessentities.MigrationSupport; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VDSGroup; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.LogCompat; import org.ovirt.engine.core.compat.LogFactoryCompat; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.utils.linq.Function; import org.ovirt.engine.core.utils.linq.LinqUtils; import org.ovirt.engine.core.utils.linq.Predicate; /** * Base class for load balancing algorithms. Load balance flow is the same for * all algorithms: Initialization all Vdss lists(under utilized, over utilized * and ready for migration), Over utilized servers treatment and under utilized * servers treatment. */ public abstract class VdsLoadBalancingAlgorithm { public VdsLoadBalancingAlgorithm(VDSGroup group) { setVdsGroup(group); } private VDSGroup privateVdsGroup; public VDSGroup getVdsGroup() { return privateVdsGroup; } private void setVdsGroup(VDSGroup value) { privateVdsGroup = value; } /** * This property contain list of all servers from db, used in load balancing * algorithm. Server must be up and without any desktop during migration */ private List<VDS> privateAllRelevantVdss; public List<VDS> getAllRelevantVdss() { return privateAllRelevantVdss; } private void setAllRelevantVdss(List<VDS> value) { privateAllRelevantVdss = value; } /** * List of all over utilized servers */ private Map<Guid, VDS> privateOverUtilizedServers; protected Map<Guid, VDS> getOverUtilizedServers() { return privateOverUtilizedServers; } protected void setOverUtilizedServers(Map<Guid, VDS> value) { privateOverUtilizedServers = value; } /** * List of all over normal servers, can be used to migrating desktops to it */ private Map<Guid, VDS> privateReadyToMigrationServers; protected Map<Guid, VDS> getReadyToMigrationServers() { return privateReadyToMigrationServers; } protected void setReadyToMigrationServers(Map<Guid, VDS> value) { privateReadyToMigrationServers = value; } /** * List of all under utilized servers. This servers can be empty to power * saving */ private Map<Guid, VDS> privateUnderUtilizedServers; protected Map<Guid, VDS> getUnderUtilizedServers() { return privateUnderUtilizedServers; } protected void setUnderUtilizedServers(Map<Guid, VDS> value) { privateUnderUtilizedServers = value; } /** * Factory method to create necessary load balancing algorithm * * @return */ public static VdsLoadBalancingAlgorithm CreateVdsLoadBalancingAlgorithm(VDSGroup group) { // return new VmCountVdsLoadBalancingAlgorithm(); return new VdsCpuVdsLoadBalancingAlgorithm(group); } public void LoadBalance() { setAllRelevantVdss(DbFacade.getInstance().getVdsDAO().getAllForVdsGroupWithoutMigrating(getVdsGroup().getID())); log.infoFormat("VdsLoadBalancer: number of relevant vdss (no migration, no pending): {0}.", getAllRelevantVdss().size()); InitOverUtilizedList(); InitReadyToMigrationList(); InitUnderUtilizedList(); if (getOverUtilizedServers().size() != 0 && (getReadyToMigrationServers().size() != 0 || getUnderUtilizedServers().size() != 0)) { ProceedOverUtilizedServers(); } if (getUnderUtilizedServers().size() > 0 && (getReadyToMigrationServers().size() > 0 || getUnderUtilizedServers().size() > 1)) { ProceedUnderUtilizedServers(); } } protected abstract void InitOverUtilizedList(); protected abstract void InitReadyToMigrationList(); protected abstract void InitUnderUtilizedList(); private void ProceedOverUtilizedServers() { // LINQ 29456 // List<int> overUtilizedServersIds = // OverUtilizedServers.Values.Select(i => i.vds_id).ToList(); List<Guid> overUtilizedServersIds = LinqUtils.foreach(getOverUtilizedServers().values(), new Function<VDS, Guid>() { @Override public Guid eval(VDS vds) { return vds.getvds_id(); } }); // LINQ 29456 for (Guid vdsId : overUtilizedServersIds) { VDS vds = getOverUtilizedServers().get(vdsId); log.infoFormat("VdsLoadBalancer: Server {0} decided as overutilized", vds.getvds_name()); java.util.List<VM> vms = getMigrableVmsRunningOnVds(vdsId); if (vms.size() != 0) { /** * Get random desktop from under utilized server and try to * migrate it to other server */ VM vm = getBestVmToMigrate(vms, vdsId); Map<Guid, VDS> currentList = getReadyToMigrationServers(); /** * Try to find server in Ready to Migration list for migrate * desktop to */ List<VDS> candidates = GetMigrationCandidates(currentList, vm); VDS destinationVds = null; if (candidates.isEmpty()) { /** * No available server in ReadyToMigrationList Try to find * server from UnderUtilized list for migrate desktop to */ currentList = getUnderUtilizedServers(); candidates = GetMigrationCandidates(currentList, vm); if (!candidates.isEmpty()) { destinationVds = candidates.get(candidates.size() - 1); } } else { destinationVds = candidates.get(0); } if (destinationVds == null) { log.infoFormat( "VdsLoadBalancer: Server {0} detected as overutilized. Failed to found another server to migrate its vms", vds.getvds_name()); } else { Guid destinationVdsId = destinationVds.getvds_id(); /** * Migrate vm from OverUtilezed server */ MigrateVmToServerParameters parameters = new MigrateVmToServerParameters(false, vm.getvm_guid(), destinationVdsId); parameters.setShouldBeLogged(false); Backend.getInstance().runInternalAction(VdcActionType.MigrateVmToServer, parameters); /** * Remove server from list */ currentList.remove(destinationVdsId); log.infoFormat("VdsLoadBalancer: Desktop {0} migrated from overutilized server {1} to server {2}", vm.getvm_name(), vds.getvds_name(), destinationVds.getvds_name()); } } else { log.info("VdsLoadBalancer: No vms found to migrate on this server"); } } } private void ProceedUnderUtilizedServers() { // LINQ 29456 // List<int> underUtilizedServersIds = // UnderUtilizedServers.Values.Select(i => i.vds_id).ToList(); List<Guid> underUtilizedServersIds = LinqUtils.foreach(getUnderUtilizedServers().values(), new Function<VDS, Guid>() { @Override public Guid eval(VDS vds) { return vds.getvds_id(); } }); Set<Guid> processed = new HashSet<Guid>(); for (Guid vdsId : underUtilizedServersIds) { if (!processed.contains(vdsId)) { VDS vds = getUnderUtilizedServers().get(vdsId); java.util.List<VM> vms = getMigrableVmsRunningOnVds(vdsId); if (vms.size() != 0) { VM vm = getBestVmToMigrate(vms, vdsId); /** * Get random desktop from under utilized server and try to * migrate it to other server */ Map<Guid, VDS> currentList = getReadyToMigrationServers(); List<VDS> candidates = GetMigrationCandidates(currentList, vm); VDS destinationVds = null; if (candidates.isEmpty()) { /** * Ready to Migrate servers not contain server to * migrate current desktop, Try to find other * UnderUtilized server with maximum count of running * desktops */ currentList = getUnderUtilizedServers(); final Guid vdsId1 = vdsId; // LINQ 29456 // candidates = GetMigrationCandidates(currentList, vm). // Where(a => a.vds_id != vdsId1). // OrderByDescending(a => a.vm_count).ToList(); candidates = LinqUtils.filter(GetMigrationCandidates(currentList, vm), new Predicate<VDS>() { @Override public boolean eval(VDS a) { return !a.getvds_id().equals(vdsId1); } }); if (!candidates.isEmpty()) { destinationVds = Collections.max(candidates, new Comparator<VDS>() { @Override public int compare(VDS o1, VDS o2) { return o1.getvm_count() - o2.getvm_count(); } }); } } else { destinationVds = candidates.get(0); } if (destinationVds == null) { log.infoFormat( "Server {0} detected as underutilized. Failed to found another server to migrate its vms", vds.getvds_name()); } else { Guid destinationVdsId = destinationVds.getvds_id(); MigrateVmToServerParameters parameters = new MigrateVmToServerParameters(false, vm.getvm_guid(), destinationVdsId); parameters.setShouldBeLogged(false); Backend.getInstance().runInternalAction(VdcActionType.MigrateVmToServer, parameters); currentList.remove(destinationVdsId); log.infoFormat( "VdsLoadBalancer: Desktop {0} migrated from underutilized server {1} to server {2}", vm.getvm_name(), vds.getvds_name(), destinationVds.getvds_name()); processed.add(destinationVdsId); } } else { log.infoFormat("VdsLoadBalancer: No vms found to migrate on this server {0}", vds.getvds_name()); } getUnderUtilizedServers().remove(vdsId); // remove the smallest // underutilized vds // which was already // processed, in // order to not // passed vm on it } } } private java.util.List<VM> getMigrableVmsRunningOnVds(Guid vdsId) { List<VM> vmsFromDB = DbFacade.getInstance().getVmDAO().getAllRunningForVds(vdsId); List<VM> vms = LinqUtils.filter(vmsFromDB, new Predicate<VM>() { @Override public boolean eval(VM v) { return v.getMigrationSupport() == MigrationSupport.MIGRATABLE; } }); return vms; } private List<VDS> GetMigrationCandidates(Map<Guid, VDS> list, final VM vm) { // LINQ return list.Values. // LINQ Where(p => p.vds_group_id == vm.vds_group_id // LINQ && RunVmCommandBase.hasMemoryToRunVM(p, vm)).ToList(); // LINQ // && !RunVmCommandBase.isVdsVersionOld(p, vm)).ToList(); return LinqUtils.filter(list.values(), new Predicate<VDS>() { @Override public boolean eval(VDS p) { return (p.getvds_group_id().equals(vm.getvds_group_id()) && RunVmCommandBase.hasMemoryToRunVM(p, vm) && RunVmCommandBase.hasCpuToRunVM(p, vm)); } }); } protected abstract VM getBestVmToMigrate(List<VM> vms, Guid vdsId); private static LogCompat log = LogFactoryCompat.getLog(VdsLoadBalancingAlgorithm.class); }