package org.ovirt.engine.core.bll; import org.ovirt.engine.core.common.businessentities.MigrationSupport; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VDSStatus; import org.ovirt.engine.core.common.businessentities.VDSType; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VMStatus; import org.ovirt.engine.core.common.businessentities.VdsVersion; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; 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.compat.NGuid; import org.ovirt.engine.core.compat.RefObject; import org.ovirt.engine.core.compat.Version; import org.ovirt.engine.core.dal.VdcBllMessages; import org.ovirt.engine.core.dal.dbbroker.DbFacade; public class VdsSelector { private java.util.ArrayList<Guid> privateRunVdssList; public java.util.ArrayList<Guid> getRunVdssList() { return privateRunVdssList; } public void setRunVdssList(java.util.ArrayList<Guid> value) { privateRunVdssList = value; } private boolean privateCheckDestinationFirst; public boolean getCheckDestinationFirst() { return privateCheckDestinationFirst; } public void setCheckDestinationFirst(boolean value) { privateCheckDestinationFirst = value; } private NGuid privateDestinationVdsId; public NGuid getDestinationVdsId() { return privateDestinationVdsId; } public void setDestinationVdsId(NGuid value) { privateDestinationVdsId = value; } private VM privateVm; private VM getVm() { return privateVm; } private void setVm(VM value) { privateVm = value; } public VdsSelector(VM vm, NGuid destinationVdsId, boolean dedicatedFirst) { setVm(vm); setDestinationVdsId(destinationVdsId); setCheckDestinationFirst(dedicatedFirst); setRunVdssList(new java.util.ArrayList<Guid>()); } public Guid GetVdsToRunOn() { Guid result = Guid.Empty; if (getDestinationVdsId() != null) { if (getCheckDestinationFirst()) { result = GetVdsRunOnDestination(); if (result.equals(Guid.Empty) && privateVm.getMigrationSupport() != MigrationSupport.PINNED_TO_HOST) { result = GetAnyVdsToRunOn(); } } else { result = GetAnyVdsToRunOn(); if (result.equals(Guid.Empty)) { result = GetVdsRunOnDestination(); } } } else { result = GetAnyVdsToRunOn(); } return result; } public boolean CanFindVdsToRunOn(java.util.ArrayList<String> messages, boolean isMigrate) { boolean returnValue = false; if (getDestinationVdsId() != null) { returnValue = CanRunOnDestinationVds(messages, isMigrate); } if (!returnValue) { if (privateVm.getMigrationSupport() == MigrationSupport.PINNED_TO_HOST) { if (messages.size() > 0) { messages.set(0, VdcBllMessages.VM_PINNED_TO_HOST_CANNOT_RUN_ON_THE_DEFAULT_VDS.toString()); } else { messages.add(VdcBllMessages.VM_PINNED_TO_HOST_CANNOT_RUN_ON_THE_DEFAULT_VDS.toString()); } return false; } returnValue = CanFindAnyVds(messages, isMigrate); } return returnValue; } private Guid GetVdsRunOnDestination() { Guid result = Guid.Empty; if (getDestinationVdsId() != null) { VDS target_vds = DbFacade.getInstance().getVdsDAO().get(getDestinationVdsId()); log.infoFormat("Checking for a specific VDS only - id:{0}, name:{1}, host_name(ip):{2}", getDestinationVdsId(), target_vds.getvds_name(), target_vds.gethost_name()); VmHandler.UpdateVmGuestAgentVersion(getVm()); if (target_vds.getvds_type() == VDSType.PowerClient && !Config.<Boolean> GetValue(ConfigValues.PowerClientAllowRunningGuestsWithoutTools) && getVm() != null && getVm().getHasAgent()) { log.infoFormat( "VdcBLL.RunVmCommandBase.getVdsToRunOn - VM {0} has no tools - skipping power client check", getVm().getvm_guid()); } else { result = getVdsToRunOn(new java.util.ArrayList<VDS>(java.util.Arrays.asList(new VDS[] { target_vds }))); } } return result; } private Guid GetAnyVdsToRunOn() { return getVdsToRunOn(DbFacade.getInstance() .getVdsDAO() .getAllOfTypes(new VDSType[] { VDSType.VDS, VDSType.oVirtNode })); } private boolean CanRunOnDestinationVds(java.util.ArrayList<String> messages, boolean isMigrate) { boolean returnValue = false; if (getDestinationVdsId() != null) { VDS target_vds = DbFacade.getInstance().getVdsDAO().get(getDestinationVdsId()); log.infoFormat("Checking for a specific VDS only - id:{0}, name:{1}, host_name(ip):{2}", getDestinationVdsId(), target_vds.getvds_name(), target_vds.gethost_name()); returnValue = CanFindVdsToRun(messages, isMigrate, new java.util.ArrayList<VDS>(java.util.Arrays.asList(new VDS[] { target_vds }))); } return returnValue; } private boolean CanFindAnyVds(java.util.ArrayList<String> messages, boolean isMigrate) { return CanFindVdsToRun(messages, isMigrate, DbFacade.getInstance().getVdsDAO().getAllOfTypes(new VDSType[] { VDSType.VDS, VDSType.oVirtNode })); } /** * This function used in CanDoAction function. Purpose is to check if there * are Vds avalable to run vm in CanDoAction - before concrete running * action This function goes over all available vdss and check if current * vds can run vm. If vds cannot running vm - reason stored. If there is no * any vds, avalable too run vm - returning reason with highest value. * Reasons sorted in VdcBllMessages by their priorities */ private boolean CanFindVdsToRun(java.util.ArrayList<String> messages, boolean isMigrate, Iterable<VDS> vdss) { VdcBllMessages message = VdcBllMessages.Unassigned; VdcBllMessages messageToReturn = VdcBllMessages.Unassigned; /** * save vdsVersion in order to know vds version that was wrong */ VdsVersion vdsVersion = null; boolean noVDSs = true; for (VDS curVds : vdss) { if (isMigrate && getVm().getrun_on_vds() != null && getVm().getrun_on_vds().equals(curVds.getvds_id())) { continue; } noVDSs = false; RefObject<VdcBllMessages> tempRefObject = new RefObject<VdcBllMessages>(message); boolean tempVar = isReadyToRun(curVds, tempRefObject); message = tempRefObject.argvalue; if (tempVar) { return true; } else { if (messageToReturn.getValue() < message.getValue()) // messageToReturn // < // message) { messageToReturn = message; /** * save version of current vds for later use */ vdsVersion = curVds.getVersion(); } } } if (noVDSs) { if (messages != null) { messageToReturn = VdcBllMessages.ACTION_TYPE_FAILED_NO_VDS_AVAILABLE_IN_CLUSTER; } } if (messages != null) { messages.add(messageToReturn.toString()); /** * if error due to versions, add versions information to can do * action message */ if (messageToReturn == VdcBllMessages.ACTION_TYPE_FAILED_VDS_VM_VERSION && vdsVersion != null) { VmHandler.UpdateVmGuestAgentVersion(getVm()); messages.add("$toolsVersion " + getVm().getPartialVersion()); messages.add("$serverVersion " + vdsVersion.getPartialVersion()); } } return false; } private boolean isReadyToRun(VDS vds, RefObject<VdcBllMessages> message) { boolean returnValue = true; if ((!vds.getvds_group_id().equals(getVm().getvds_group_id())) || (vds.getstatus() != VDSStatus.Up) || isVdsFailedToRunVm(vds.getvds_id())) { returnValue = false; message.argvalue = VdcBllMessages.ACTION_TYPE_FAILED_VDS_VM_CLUSTER; } // If Vm in Paused mode - no additional memory allocation needed else if (getVm().getstatus() != VMStatus.Paused && !RunVmCommandBase.hasMemoryToRunVM(vds, getVm())) { // not enough memory // In case we are using this function in migration we make sure we // don't allocate the same VDS returnValue = false; message.argvalue = VdcBllMessages.ACTION_TYPE_FAILED_VDS_VM_MEMORY; } // if vm has more vCpus then vds physical cpus - dont allow to run else if (vds.getcpu_cores() != null && getVm().getnum_of_cpus() > vds.getcpu_cores()) { returnValue = false; message.argvalue = VdcBllMessages.ACTION_TYPE_FAILED_VDS_VM_CPUS; } // else if (RunVmCommandBase.isVdsVersionOld(vds, Vm)) // { // returnValue = false; // message = VdcBllMessages.ACTION_TYPE_FAILED_VDS_VM_VERSION; // } else if (!IsVMSwapValueLegal(vds)) { returnValue = false; message.argvalue = VdcBllMessages.ACTION_TYPE_FAILED_VDS_VM_SWAP; } return returnValue; } /** * Determine if specific vds already failed to run vm - to prevent * sequentual running of vm on problematic vds * * @param vdsId * @return */ private boolean isVdsFailedToRunVm(Guid vdsId) { boolean retValue = false; if (getRunVdssList() != null && getRunVdssList().contains(vdsId)) { retValue = true; } return retValue; } /** * Determines whether [is VM swap value legal] [the specified VDS]. * * @param vds * The VDS. * @return <c>true</c> if [is VM swap value legal] [the specified VDS]; * otherwise, <c>false</c>. */ private static boolean IsVMSwapValueLegal(VDS vds) { Version version = vds.getvds_group_compatibility_version(); if (!Config.<Boolean> GetValue(ConfigValues.EnableSwapCheck)) { return true; } if (vds.getswap_total() == null || vds.getswap_free() == null || vds.getmem_available() == null || vds.getmem_available() <= 0 || vds.getphysical_mem_mb() == null || vds.getphysical_mem_mb() <= 0) { return true; } long swap_total = vds.getswap_total(); long swap_free = vds.getswap_free(); long mem_available = vds.getmem_available(); long physical_mem_mb = vds.getphysical_mem_mb(); return ((swap_total - swap_free - mem_available) * 100 / physical_mem_mb) <= Config .<Integer> GetValue(ConfigValues.BlockMigrationOnSwapUsagePercentage); } private Guid getVdsToRunOn(Iterable<VDS> vdss) { java.util.ArrayList<VDS> readyToRun = new java.util.ArrayList<VDS>(); for (VDS curVds : vdss) { // vds must be in the correct group if (!curVds.getvds_group_id().equals(getVm().getvds_group_id())) continue; // vds must be up to run a vm if (curVds.getstatus() != VDSStatus.Up) continue; // apply limit on vds memory over commit. if (!RunVmCommandBase.hasMemoryToRunVM(curVds, getVm())) continue; // In case we are using this function in migration we make sure we // don't allocate the same VDS if ((getVm().getrun_on_vds() != null && getVm().getrun_on_vds().equals(curVds.getvds_id())) || isVdsFailedToRunVm(curVds.getvds_id()) || // RunVmCommandBase.isVdsVersionOld(curVds, getVm()) || !RunVmCommandBase.hasCapacityToRunVM(curVds)) continue; // vds must have at least cores as the vm if (curVds.getcpu_cores() != null && getVm().getnum_of_cpus() > curVds.getcpu_cores()) { continue; } if (!IsVMSwapValueLegal(curVds)) continue; readyToRun.add(curVds); } return readyToRun.isEmpty() ? Guid.Empty : getBestVdsToRun(readyToRun); } private Guid getBestVdsToRun(java.util.ArrayList<VDS> list) { VdsComparer comparer = VdsComparer.CreateComparer(list.get(0).getselection_algorithm()); VDS bestVDS = list.get(0); for (int i = 1; i < list.size(); i++) { VDS curVds = list.get(i); if (comparer.IsBetter(bestVDS, curVds, getVm())) // if (((bestVDS.physical_mem_mb - bestVDS.mem_commited) < // (curVds.physical_mem_mb - curVds.mem_commited))) { bestVDS = curVds; } } /** * add chosen vds to running vdss list. */ comparer.BestVdsProcedure(bestVDS); getRunVdssList().add(bestVDS.getvds_id()); return bestVDS.getvds_id(); } private static LogCompat log = LogFactoryCompat.getLog(VdsSelector.class); }