Ensure affinity groups are honored when VMs are deployed in parallel (#9201)

This commit is contained in:
Vishesh 2024-08-12 17:32:16 +05:30 committed by GitHub
parent bf11676278
commit c98f1b8b3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 120 additions and 56 deletions

View File

@ -38,4 +38,7 @@ public interface AffinityGroupDao extends GenericDao<AffinityGroupVO, Long> {
AffinityGroupVO findByAccountAndType(Long accountId, String string);
AffinityGroupVO findDomainLevelGroupByType(Long domainId, String string);
List<AffinityGroupVO> listByIds(List<Long> ids, boolean exclusive);
}

View File

@ -31,6 +31,7 @@ import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
public class AffinityGroupDaoImpl extends GenericDaoBase<AffinityGroupVO, Long> implements AffinityGroupDao {
private SearchBuilder<AffinityGroupVO> IdsSearch;
private SearchBuilder<AffinityGroupVO> AccountIdSearch;
private SearchBuilder<AffinityGroupVO> AccountIdNameSearch;
private SearchBuilder<AffinityGroupVO> AccountIdNamesSearch;
@ -47,6 +48,10 @@ public class AffinityGroupDaoImpl extends GenericDaoBase<AffinityGroupVO, Long>
@PostConstruct
protected void init() {
IdsSearch = createSearchBuilder();
IdsSearch.and("idIn", IdsSearch.entity().getId(), SearchCriteria.Op.IN);
IdsSearch.done();
AccountIdSearch = createSearchBuilder();
AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
AccountIdSearch.done();
@ -158,4 +163,11 @@ public class AffinityGroupDaoImpl extends GenericDaoBase<AffinityGroupVO, Long>
sc.setJoinParameters("domainTypeSearch", "domainId", domainId);
return findOneBy(sc);
}
@Override
public List<AffinityGroupVO> listByIds(List<Long> ids, boolean exclusive) {
SearchCriteria<AffinityGroupVO> sc = IdsSearch.create();
sc.setParameters("idIn", ids.toArray());
return lockRows(sc, null, exclusive);
}
}

View File

@ -23,9 +23,14 @@ import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.stream.Collectors;
import javax.inject.Inject;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.TransactionStatus;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
@ -56,10 +61,17 @@ public class HostAffinityProcessor extends AffinityProcessorBase implements Affi
VirtualMachine vm = vmProfile.getVirtualMachine();
List<AffinityGroupVMMapVO> vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), getType());
if (CollectionUtils.isNotEmpty(vmGroupMappings)) {
List<Long> affinityGroupIdList = vmGroupMappings.stream().map(AffinityGroupVMMapVO::getAffinityGroupId).collect(Collectors.toList());
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
_affinityGroupDao.listByIds(affinityGroupIdList, true);
for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) {
processAffinityGroup(vmGroupMapping, plan, vm, vmList);
}
}
});
}
}
@ -132,17 +144,24 @@ public class HostAffinityProcessor extends AffinityProcessorBase implements Affi
long plannedHostId = plannedDestination.getHost().getId();
VirtualMachine vm = vmProfile.getVirtualMachine();
List<AffinityGroupVMMapVO> vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), getType());
if (CollectionUtils.isNotEmpty(vmGroupMappings)) {
if (CollectionUtils.isEmpty(vmGroupMappings)) {
return true;
}
List<Long> affinityGroupIds = vmGroupMappings.stream().map(AffinityGroupVMMapVO::getAffinityGroupId).collect(Collectors.toList());
return Transaction.execute(new TransactionCallback<Boolean>() {
@Override
public Boolean doInTransaction(TransactionStatus status) {
_affinityGroupDao.listByIds(affinityGroupIds, true);
for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) {
if (!checkAffinityGroup(vmGroupMapping, vm, plannedHostId)) {
return false;
}
}
}
}
return true;
}
});
}
/**
* Check Affinity Group

View File

@ -19,10 +19,17 @@ package org.apache.cloudstack.affinity;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.utils.DateUtil;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.TransactionStatus;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
@ -36,7 +43,6 @@ import com.cloud.deploy.DeployDestination;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.exception.AffinityConflictException;
import com.cloud.utils.DateUtil;
import com.cloud.utils.NumbersUtil;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
@ -67,7 +73,23 @@ public class HostAntiAffinityProcessor extends AffinityProcessorBase implements
VirtualMachine vm = vmProfile.getVirtualMachine();
List<AffinityGroupVMMapVO> vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), getType());
if (CollectionUtils.isEmpty(vmGroupMappings)) {
return;
}
List<Long> affinityGroupIds = vmGroupMappings.stream().map(AffinityGroupVMMapVO::getAffinityGroupId).collect(Collectors.toList());
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
_affinityGroupDao.listByIds(affinityGroupIds, true);
for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) {
processAffinityGroup(vmGroupMapping, avoid, vm);
}
}
});
}
protected void processAffinityGroup(AffinityGroupVMMapVO vmGroupMapping, ExcludeList avoid, VirtualMachine vm) {
if (vmGroupMapping != null) {
AffinityGroupVO group = _affinityGroupDao.findById(vmGroupMapping.getAffinityGroupId());
@ -86,6 +108,7 @@ public class HostAntiAffinityProcessor extends AffinityProcessorBase implements
if (s_logger.isDebugEnabled()) {
s_logger.debug("Added host " + groupVM.getHostId() + " to avoid set, since VM " + groupVM.getId() + " is present on the host");
}
}
} else if (Arrays.asList(VirtualMachine.State.Starting, VirtualMachine.State.Stopped).contains(groupVM.getState()) && groupVM.getLastHostId() != null) {
long secondsSinceLastUpdate = (DateUtil.currentGMTTime().getTime() - groupVM.getUpdateTime().getTime()) / 1000;
if (secondsSinceLastUpdate < _vmCapacityReleaseInterval) {
@ -99,9 +122,6 @@ public class HostAntiAffinityProcessor extends AffinityProcessorBase implements
}
}
}
}
}
@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
@ -121,7 +141,15 @@ public class HostAntiAffinityProcessor extends AffinityProcessorBase implements
VirtualMachine vm = vmProfile.getVirtualMachine();
List<AffinityGroupVMMapVO> vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), getType());
if (CollectionUtils.isEmpty(vmGroupMappings)) {
return true;
}
List<Long> affinityGroupIds = vmGroupMappings.stream().map(AffinityGroupVMMapVO::getAffinityGroupId).collect(Collectors.toList());
return Transaction.execute(new TransactionCallback<Boolean>() {
@Override
public Boolean doInTransaction(TransactionStatus status) {
_affinityGroupDao.listByIds(affinityGroupIds, true);
for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) {
// if more than 1 VM's are present in the group then check for
// conflict due to parallel deployment
@ -141,5 +169,7 @@ public class HostAntiAffinityProcessor extends AffinityProcessorBase implements
}
return true;
}
});
}
}