package org.ovirt.engine.core.bll.scheduling.policyunits;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.math.NumberUtils;
import org.ovirt.engine.core.bll.scheduling.PolicyUnitImpl;
import org.ovirt.engine.core.bll.scheduling.PolicyUnitParameter;
import org.ovirt.engine.core.bll.scheduling.SchedulingUnit;
import org.ovirt.engine.core.bll.scheduling.pending.PendingResourceManager;
import org.ovirt.engine.core.common.businessentities.Cluster;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.scheduling.PerHostMessages;
import org.ovirt.engine.core.common.scheduling.PolicyUnit;
import org.ovirt.engine.core.common.scheduling.PolicyUnitType;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.dao.VmDao;
/**
* Make sure there is at least a configured amount of HE enabled
* hosts that have enough memory to run the engine VM in case the
* current host dies.
*
* The logic
* - does nothing when this is not an hosted engine deployment
* - does nothing when the engine VM is (re-)scheduled
* - does not touch any non-HE hosts
* - does nothing when there are enough HE spares
* - removes all HE hosts when there are not enough spares
*/
@SchedulingUnit(
guid = "53bff075-8306-446f-a53b-9c872a29d197",
name = "HostedEngineSpares",
description = "Reserve space for starting the hosted engine VM on different hosts in case "
+ "the current one crashes.",
type = PolicyUnitType.FILTER,
parameters = {
PolicyUnitParameter.HE_SPARES_COUNT
}
)
public class HostedEngineMemoryReservationFilterPolicyUnit extends PolicyUnitImpl {
public HostedEngineMemoryReservationFilterPolicyUnit(PolicyUnit policyUnit,
PendingResourceManager pendingResourceManager) {
super(policyUnit, pendingResourceManager);
}
@Override
public List<VDS> filter(Cluster cluster, List<VDS> hosts, VM vm, Map<String, String> parameters, PerHostMessages messages) {
// Hosts available for running the `vm`
Set<VDS> candidateHosts = new HashSet<>();
// Hosts needed as spares for the hosted engine
Set<VDS> spares = new HashSet<>();
final int requiredSpares = NumberUtils.toInt(parameters.get(PolicyUnitParameter.HE_SPARES_COUNT.getDbName()), 0);
// There are no hosts, skip this unit
if (hosts.isEmpty()) {
return hosts;
}
// No spares requested, skip this unit
if (requiredSpares == 0) {
return hosts;
}
// Get the instance of hosted engine VM so we can get the amount of memory that is needed
VM hostedEngine = vmDao().getHostedEngineVm();
// Not a hosted engine deployment, ignore this unit
if (hostedEngine == null) {
return hosts;
}
// The hosted engine VM is not part of the currently scheduled cluster
if (!hostedEngine.getClusterId().equals(vm.getClusterId())) {
return hosts;
}
// Scheduling the hosted engine VM, do nothing
if (hostedEngine.getId().equals(vm.getId())) {
return hosts;
}
// Count the number of hosted engine spares
for (VDS host: hosts) {
// Not a HE host
if (!host.getHighlyAvailableIsActive()) {
candidateHosts.add(host);
// HE host that can't run the engine at this moment because of score
} else if (host.getHighlyAvailableScore() == 0) {
candidateHosts.add(host);
// HE host in local maintenance can't be used as a spare
} else if (host.getHighlyAvailableLocalMaintenance()) {
candidateHosts.add(host);
// HE host where the engine is currently running
} else if (host.getId().equals(hostedEngine.getRunOnVds())) {
candidateHosts.add(host);
// HE host that has enough memory to run both the hosted engine VM
// and the scheduled VM at the same time -- count as candidate and spare!
} else if (host.getMaxSchedulingMemory()
> vm.getMemSizeMb() + 2 * host.getGuestOverhead() + hostedEngine.getMemSizeMb()) {
spares.add(host);
candidateHosts.add(host);
// HE host that has enough memory to run the hosted engine VM -- count as spare only!
} else if (host.getMaxSchedulingMemory() > hostedEngine.getMemSizeMb() + host.getGuestOverhead()) {
spares.add(host);
} else {
candidateHosts.add(host);
}
}
if (spares.size() <= requiredSpares) {
// We have the right amount of spares (or less) and the spares have to be kept reserved
// for the hosted engine
for (VDS host: spares) {
messages.addMessage(host.getId(), EngineMessage.VAR__DETAIL__NOT_ENOUGH_HE_SPARES.name());
}
} else {
// There are more spares than necessary, one less won't hurt anything
candidateHosts.addAll(spares);
}
return new ArrayList<>(candidateHosts);
}
protected VmDao vmDao() {
return DbFacade.getInstance().getVmDao();
}
}