server,engine-orchestration: allocate vm without transaction (#7695)

When deploying a VM is failed during the allocation process it may leave the resources that have been already allocated before the failure. They will get removed from the database as the whole code block is wrapped inside a transaction twice but the server would not inform the network or storage plugins to clean up the allocated resources.
This PR removes Transactions during VM allocation which results in the allocated VM and its resource records being persisted in DB even during failures. When failure is encountered VM is moved to Error state. This helps VM and its resources to be properly deallocated when it is expunged either by a server task such as ExpungeTask or during manual expunge.

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2023-07-06 14:04:38 +05:30 committed by GitHub
parent c733a23c90
commit 939ee9e153
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 201 additions and 200 deletions

View File

@ -458,7 +458,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
throws InsufficientCapacityException { throws InsufficientCapacityException {
s_logger.info(String.format("allocating virtual machine from template:%s with hostname:%s and %d networks", template.getUuid(), vmInstanceName, auxiliaryNetworks.size())); s_logger.info(String.format("allocating virtual machine from template:%s with hostname:%s and %d networks", template.getUuid(), vmInstanceName, auxiliaryNetworks.size()));
VMInstanceVO persistedVm = null;
try {
final VMInstanceVO vm = _vmDao.findVMByInstanceName(vmInstanceName); final VMInstanceVO vm = _vmDao.findVMByInstanceName(vmInstanceName);
final Account owner = _entityMgr.findById(Account.class, vm.getAccountId()); final Account owner = _entityMgr.findById(Account.class, vm.getAccountId());
@ -471,9 +472,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
vm.setPodIdToDeployIn(plan.getPodId()); vm.setPodIdToDeployIn(plan.getPodId());
} }
assert plan.getClusterId() == null && plan.getPoolId() == null : "We currently don't support cluster and pool preset yet"; assert plan.getClusterId() == null && plan.getPoolId() == null : "We currently don't support cluster and pool preset yet";
final VMInstanceVO vmFinal = _vmDao.persist(vm); persistedVm = _vmDao.persist(vm);
final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmFinal, template, serviceOffering, null, null); final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(persistedVm, template, serviceOffering, null, null);
Long rootDiskSize = rootDiskOfferingInfo.getSize(); Long rootDiskSize = rootDiskOfferingInfo.getSize();
if (vm.getType().isUsedBySystem() && SystemVmRootDiskSize.value() != null && SystemVmRootDiskSize.value() > 0L) { if (vm.getType().isUsedBySystem() && SystemVmRootDiskSize.value() != null && SystemVmRootDiskSize.value() > 0L) {
@ -481,11 +482,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
} }
final Long rootDiskSizeFinal = rootDiskSize; final Long rootDiskSizeFinal = rootDiskSize;
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<InsufficientCapacityException>() {
@Override
public void doInTransactionWithoutResult(final TransactionStatus status) throws InsufficientCapacityException {
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
s_logger.debug("Allocating nics for " + vmFinal); s_logger.debug("Allocating nics for " + persistedVm);
} }
try { try {
@ -497,15 +495,15 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
} }
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
s_logger.debug("Allocating disks for " + vmFinal); s_logger.debug("Allocating disks for " + persistedVm);
} }
allocateRootVolume(vmFinal, template, rootDiskOfferingInfo, owner, rootDiskSizeFinal); allocateRootVolume(persistedVm, template, rootDiskOfferingInfo, owner, rootDiskSizeFinal);
if (dataDiskOfferings != null) { if (dataDiskOfferings != null) {
for (final DiskOfferingInfo dataDiskOfferingInfo : dataDiskOfferings) { for (final DiskOfferingInfo dataDiskOfferingInfo : dataDiskOfferings) {
volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + vmFinal.getId(), dataDiskOfferingInfo.getDiskOffering(), dataDiskOfferingInfo.getSize(), volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + persistedVm.getId(), dataDiskOfferingInfo.getDiskOffering(), dataDiskOfferingInfo.getSize(),
dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), vmFinal, template, owner, null); dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, null);
} }
} }
if (datadiskTemplateToDiskOfferingMap != null && !datadiskTemplateToDiskOfferingMap.isEmpty()) { if (datadiskTemplateToDiskOfferingMap != null && !datadiskTemplateToDiskOfferingMap.isEmpty()) {
@ -514,16 +512,25 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
DiskOffering diskOffering = dataDiskTemplateToDiskOfferingMap.getValue(); DiskOffering diskOffering = dataDiskTemplateToDiskOfferingMap.getValue();
long diskOfferingSize = diskOffering.getDiskSize() / (1024 * 1024 * 1024); long diskOfferingSize = diskOffering.getDiskSize() / (1024 * 1024 * 1024);
VMTemplateVO dataDiskTemplate = _templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey()); VMTemplateVO dataDiskTemplate = _templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey());
volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + vmFinal.getId() + "-" + String.valueOf(diskNumber), diskOffering, diskOfferingSize, null, null, volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + persistedVm.getId() + "-" + String.valueOf(diskNumber), diskOffering, diskOfferingSize, null, null,
vmFinal, dataDiskTemplate, owner, Long.valueOf(diskNumber)); persistedVm, dataDiskTemplate, owner, Long.valueOf(diskNumber));
diskNumber++; diskNumber++;
} }
} }
}
});
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
s_logger.debug("Allocation completed for VM: " + vmFinal); s_logger.debug("Allocation completed for VM: " + persistedVm);
}
} catch (InsufficientCapacityException | CloudRuntimeException e) {
// Failed VM will be in Stopped. Transition it to Error, so it can be expunged by ExpungeTask or similar
try {
if (persistedVm != null) {
stateTransitTo(persistedVm, VirtualMachine.Event.OperationFailedToError, null);
}
} catch (NoTransitionException nte) {
s_logger.error(String.format("Failed to transition %s in %s state to Error state", persistedVm, persistedVm.getState().toString()));
}
throw e;
} }
} }

View File

@ -348,7 +348,6 @@ import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction; import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.TransactionCallbackWithException;
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.db.UUIDManager; import com.cloud.utils.db.UUIDManager;
@ -4366,9 +4365,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map<String, String> customParameters, final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map<String, String> customParameters,
final Map<String, Map<Integer, String>> extraDhcpOptionMap, final Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, final Map<String, Map<Integer, String>> extraDhcpOptionMap, final Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
final Map<String, String> userVmOVFPropertiesMap, final VirtualMachine.PowerState powerState, final boolean dynamicScalingEnabled, String vmType, final Long rootDiskOfferingId, String sshkeypairs) throws InsufficientCapacityException { final Map<String, String> userVmOVFPropertiesMap, final VirtualMachine.PowerState powerState, final boolean dynamicScalingEnabled, String vmType, final Long rootDiskOfferingId, String sshkeypairs) throws InsufficientCapacityException {
return Transaction.execute(new TransactionCallbackWithException<UserVmVO, InsufficientCapacityException>() {
@Override
public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCapacityException {
UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.isOfferHA(), UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.isOfferHA(),
offering.getLimitCpuUse(), owner.getDomainId(), owner.getId(), userId, offering.getId(), userData, userDataId, userDataDetails, hostName); offering.getLimitCpuUse(), owner.getDomainId(), owner.getId(), userId, offering.getId(), userData, userDataId, userDataDetails, hostName);
vm.setUuid(uuidName); vm.setUuid(uuidName);
@ -4520,8 +4516,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
} }
return vm; return vm;
} }
});
}
private void updateVMDiskController(UserVmVO vm, Map<String, String> customParameters, GuestOSVO guestOS) { private void updateVMDiskController(UserVmVO vm, Map<String, String> customParameters, GuestOSVO guestOS) {
// If hypervisor is vSphere and OS is OS X, set special settings. // If hypervisor is vSphere and OS is OS X, set special settings.