CLOUDSTACK-9738: [Vmware] Optimize vm expunge process for instances with vm snapshots

This commit is contained in:
nvazquez 2017-01-09 17:08:32 -03:00 committed by nvazquez
parent 8e069ed132
commit 6ce6cf67f0
18 changed files with 93 additions and 27 deletions

View File

@ -75,14 +75,14 @@ public interface UserVmService {
/** /**
* Destroys one virtual machine * Destroys one virtual machine
* *
* @param userId
* the id of the user performing the action
* @param vmId * @param vmId
* the id of the virtual machine. * the id of the virtual machine.
* @param expunge
* indicates if vm should be expunged
* @throws ConcurrentOperationException * @throws ConcurrentOperationException
* @throws ResourceUnavailableException * @throws ResourceUnavailableException
*/ */
UserVm destroyVm(long vmId) throws ResourceUnavailableException, ConcurrentOperationException; UserVm destroyVm(long vmId, boolean expunge) throws ResourceUnavailableException, ConcurrentOperationException;
/** /**
* Resets the password of a virtual machine. * Resets the password of a virtual machine.

View File

@ -46,4 +46,11 @@ public interface VMSnapshotService {
ConcurrentOperationException; ConcurrentOperationException;
VirtualMachine getVMBySnapshotId(Long id); VirtualMachine getVMBySnapshotId(Long id);
/**
* Delete vm snapshots only from database. Introduced as a Vmware optimization in which vm snapshots are deleted when
* the vm gets deleted on hypervisor (no need to delete each vm snapshot before deleting vm, just mark them as deleted on DB)
* @param id vm id
*/
boolean deleteVMSnapshotsFromDB(Long vmId);
} }

View File

@ -110,7 +110,7 @@ public interface VirtualMachineManager extends Manager {
void advanceExpunge(String vmUuid) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException; void advanceExpunge(String vmUuid) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException;
void destroy(String vmUuid) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException; void destroy(String vmUuid, boolean expunge) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException;
void migrateAway(String vmUuid, long hostId) throws InsufficientServerCapacityException; void migrateAway(String vmUuid, long hostId) throws InsufficientServerCapacityException;

View File

@ -129,8 +129,9 @@ public interface VirtualMachineEntity extends CloudStackEntity {
/** /**
* Destroys the VM. * Destroys the VM.
* @param expunge indicates if vm should be expunged
*/ */
boolean destroy(String caller) throws AgentUnavailableException, CloudException, ConcurrentOperationException; boolean destroy(String caller, boolean expunge) throws AgentUnavailableException, CloudException, ConcurrentOperationException;
/** /**
* Duplicate this VM in the database so that it will start new * Duplicate this VM in the database so that it will start new

View File

@ -28,4 +28,12 @@ public interface VMSnapshotStrategy {
boolean revertVMSnapshot(VMSnapshot vmSnapshot); boolean revertVMSnapshot(VMSnapshot vmSnapshot);
StrategyPriority canHandle(VMSnapshot vmSnapshot); StrategyPriority canHandle(VMSnapshot vmSnapshot);
/**
* Delete vm snapshot only from database. Introduced as a Vmware optimization in which vm snapshots are deleted when
* the vm gets deleted on hypervisor (no need to delete each vm snapshot before deleting vm, just mark them as deleted on DB)
* @param vmSnapshot vm snapshot to be marked as deleted.
* @return true if vm snapshot removed from DB, false if not.
*/
boolean deleteVMSnapshotFromDB(VMSnapshot vmSnapshot);
} }

View File

@ -1679,7 +1679,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
} }
@Override @Override
public void destroy(final String vmUuid) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { public void destroy(final String vmUuid, final boolean expunge) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException {
VMInstanceVO vm = _vmDao.findByUuid(vmUuid); VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging || vm.getRemoved() != null) { if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging || vm.getRemoved() != null) {
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
@ -1689,15 +1689,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
} }
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
s_logger.debug("Destroying vm " + vm); s_logger.debug("Destroying vm " + vm + ", expunge flag " + (expunge ? "on" : "off"));
} }
advanceStop(vmUuid, VmDestroyForcestop.value()); advanceStop(vmUuid, VmDestroyForcestop.value());
if (!_vmSnapshotMgr.deleteAllVMSnapshots(vm.getId(), null)) { deleteVMSnapshots(vm, expunge);
s_logger.debug("Unable to delete all snapshots for " + vm);
throw new CloudRuntimeException("Unable to delete vm snapshots for " + vm);
}
// reload the vm object from db // reload the vm object from db
vm = _vmDao.findByUuid(vmUuid); vm = _vmDao.findByUuid(vmUuid);
@ -1712,6 +1709,27 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
} }
} }
/**
* Delete vm snapshots depending on vm's hypervisor type. For Vmware, vm snapshots removal is delegated to vm cleanup thread
* to reduce tasks sent to hypervisor (one tasks to delete vm snapshots and vm itself
* instead of one task for each vm snapshot plus another for the vm)
* @param vm vm
* @param expunge indicates if vm should be expunged
*/
private void deleteVMSnapshots(VMInstanceVO vm, boolean expunge) {
if (! vm.getHypervisorType().equals(HypervisorType.VMware)) {
if (!_vmSnapshotMgr.deleteAllVMSnapshots(vm.getId(), null)) {
s_logger.debug("Unable to delete all snapshots for " + vm);
throw new CloudRuntimeException("Unable to delete vm snapshots for " + vm);
}
}
else {
if (expunge) {
_vmSnapshotMgr.deleteVMSnapshotsFromDB(vm.getId());
}
}
}
protected boolean checkVmOnHost(final VirtualMachine vm, final long hostId) throws AgentUnavailableException, OperationTimedoutException { protected boolean checkVmOnHost(final VirtualMachine vm, final long hostId) throws AgentUnavailableException, OperationTimedoutException {
final Answer answer = _agentMgr.send(hostId, new CheckVirtualMachineCommand(vm.getInstanceName())); final Answer answer = _agentMgr.send(hostId, new CheckVirtualMachineCommand(vm.getInstanceName()));
if (answer == null || !answer.getResult()) { if (answer == null || !answer.getResult()) {

View File

@ -46,5 +46,5 @@ public interface VMEntityManager {
boolean stopvirtualmachineforced(VMEntityVO vmEntityVO, String caller) throws ResourceUnavailableException; boolean stopvirtualmachineforced(VMEntityVO vmEntityVO, String caller) throws ResourceUnavailableException;
boolean destroyVirtualMachine(VMEntityVO vmEntityVO, String caller) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException; boolean destroyVirtualMachine(VMEntityVO vmEntityVO, String caller, boolean expunge) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException;
} }

View File

@ -261,10 +261,10 @@ public class VMEntityManagerImpl implements VMEntityManager {
} }
@Override @Override
public boolean destroyVirtualMachine(VMEntityVO vmEntityVO, String caller) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { public boolean destroyVirtualMachine(VMEntityVO vmEntityVO, String caller, boolean expunge) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException {
VMInstanceVO vm = _vmDao.findByUuid(vmEntityVO.getUuid()); VMInstanceVO vm = _vmDao.findByUuid(vmEntityVO.getUuid());
_itMgr.destroy(vm.getUuid()); _itMgr.destroy(vm.getUuid(), expunge);
return true; return true;
} }

View File

@ -229,8 +229,8 @@ public class VirtualMachineEntityImpl implements VirtualMachineEntity {
} }
@Override @Override
public boolean destroy(String caller) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { public boolean destroy(String caller, boolean expunge) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException {
return manager.destroyVirtualMachine(this.vmEntityVO, caller); return manager.destroyVirtualMachine(this.vmEntityVO, caller, expunge);
} }
@Override @Override

View File

@ -392,4 +392,15 @@ public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshot
public StrategyPriority canHandle(VMSnapshot vmSnapshot) { public StrategyPriority canHandle(VMSnapshot vmSnapshot) {
return StrategyPriority.DEFAULT; return StrategyPriority.DEFAULT;
} }
@Override
public boolean deleteVMSnapshotFromDB(VMSnapshot vmSnapshot) {
try {
vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.ExpungeRequested);
} catch (NoTransitionException e) {
s_logger.debug("Failed to change vm snapshot state with event ExpungeRequested");
throw new CloudRuntimeException("Failed to change vm snapshot state with event ExpungeRequested: " + e.getMessage());
}
return vmSnapshotDao.remove(vmSnapshot.getId());
}
} }

View File

@ -1679,9 +1679,6 @@ public class VmwareStorageProcessor implements StorageProcessor {
s_logger.info("Destroy root volume and VM itself. vmName " + vmName); s_logger.info("Destroy root volume and VM itself. vmName " + vmName);
} }
// Remove all snapshots to consolidate disks for removal
vmMo.removeAllSnapshots();
VirtualMachineDiskInfo diskInfo = null; VirtualMachineDiskInfo diskInfo = null;
if (vol.getChainInfo() != null) if (vol.getChainInfo() != null)
diskInfo = _gson.fromJson(vol.getChainInfo(), VirtualMachineDiskInfo.class); diskInfo = _gson.fromJson(vol.getChainInfo(), VirtualMachineDiskInfo.class);

View File

@ -674,7 +674,7 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements HighAvai
} }
if (vm.getHostId() != null) { if (vm.getHostId() != null) {
_itMgr.destroy(vm.getUuid()); _itMgr.destroy(vm.getUuid(), false);
s_logger.info("Successfully destroy " + vm); s_logger.info("Successfully destroy " + vm);
return null; return null;
} else { } else {

View File

@ -1514,7 +1514,7 @@ public class AutoScaleManagerImpl<Type> extends ManagerBase implements AutoScale
public void run() { public void run() {
try { try {
_userVmManager.destroyVm(vmId); _userVmManager.destroyVm(vmId, false);
} catch (ResourceUnavailableException e) { } catch (ResourceUnavailableException e) {
e.printStackTrace(); e.printStackTrace();

View File

@ -2165,7 +2165,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
final List<VMInstanceVO> vmsOnLocalStorage = _storageMgr.listByStoragePool(storagePool.getId()); final List<VMInstanceVO> vmsOnLocalStorage = _storageMgr.listByStoragePool(storagePool.getId());
for (final VMInstanceVO vm : vmsOnLocalStorage) { for (final VMInstanceVO vm : vmsOnLocalStorage) {
try { try {
_vmMgr.destroy(vm.getUuid()); _vmMgr.destroy(vm.getUuid(), false);
} catch (final Exception e) { } catch (final Exception e) {
final String errorMsg = "There was an error Destory the vm: " + vm + " as a part of hostDelete id=" + host.getId(); final String errorMsg = "There was an error Destory the vm: " + vm + " as a part of hostDelete id=" + host.getId();
s_logger.debug(errorMsg, e); s_logger.debug(errorMsg, e);

View File

@ -759,7 +759,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
for (UserVmVO vm : vms) { for (UserVmVO vm : vms) {
if (vm.getState() != VirtualMachine.State.Destroyed && vm.getState() != VirtualMachine.State.Expunging) { if (vm.getState() != VirtualMachine.State.Destroyed && vm.getState() != VirtualMachine.State.Expunging) {
try { try {
_vmMgr.destroyVm(vm.getId()); _vmMgr.destroyVm(vm.getId(), false);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
s_logger.warn("Failed destroying instance " + vm.getUuid() + " as part of account deletion."); s_logger.warn("Failed destroying instance " + vm.getUuid() + " as part of account deletion.");

8
server/src/com/cloud/vm/UserVmManagerImpl.java Normal file → Executable file
View File

@ -2627,7 +2627,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
throw new PermissionDeniedException("Parameter " + ApiConstants.EXPUNGE + " can be passed by Admin only. Or when the allow.user.expunge.recover.vm key is set."); throw new PermissionDeniedException("Parameter " + ApiConstants.EXPUNGE + " can be passed by Admin only. Or when the allow.user.expunge.recover.vm key is set.");
} }
UserVm destroyedVm = destroyVm(vmId); UserVm destroyedVm = destroyVm(vmId, expunge);
if (expunge) { if (expunge) {
UserVmVO vm = _vmDao.findById(vmId); UserVmVO vm = _vmDao.findById(vmId);
if (!expunge(vm, ctx.getCallingUserId(), ctx.getCallingAccount())) { if (!expunge(vm, ctx.getCallingUserId(), ctx.getCallingAccount())) {
@ -4114,7 +4114,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
} }
@Override @Override
public UserVm destroyVm(long vmId) throws ResourceUnavailableException, ConcurrentOperationException { public UserVm destroyVm(long vmId, boolean expunge) throws ResourceUnavailableException, ConcurrentOperationException {
// Account caller = CallContext.current().getCallingAccount(); // Account caller = CallContext.current().getCallingAccount();
// Long userId = CallContext.current().getCallingUserId(); // Long userId = CallContext.current().getCallingUserId();
Long userId = 2L; Long userId = 2L;
@ -4136,7 +4136,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
try { try {
VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid());
status = vmEntity.destroy(Long.toString(userId)); status = vmEntity.destroy(Long.toString(userId), expunge);
} catch (CloudException e) { } catch (CloudException e) {
CloudRuntimeException ex = new CloudRuntimeException("Unable to destroy with specified vmId", e); CloudRuntimeException ex = new CloudRuntimeException("Unable to destroy with specified vmId", e);
ex.addProxyObject(vm.getUuid(), "vmId"); ex.addProxyObject(vm.getUuid(), "vmId");
@ -4318,6 +4318,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
throw new PermissionDeniedException("Expunging a vm can only be done by an Admin. Or when the allow.user.expunge.recover.vm key is set."); throw new PermissionDeniedException("Expunging a vm can only be done by an Admin. Or when the allow.user.expunge.recover.vm key is set.");
} }
_vmSnapshotMgr.deleteVMSnapshotsFromDB(vmId);
boolean status; boolean status;
status = expunge(vm, userId, caller); status = expunge(vm, userId, caller);

View File

@ -1133,4 +1133,25 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
} }
return result; return result;
} }
@Override
public boolean deleteVMSnapshotsFromDB(Long vmId) {
List<VMSnapshotVO> listVmSnapshots = _vmSnapshotDao.findByVm(vmId);
if (listVmSnapshots == null || listVmSnapshots.isEmpty()) {
return true;
}
for (VMSnapshotVO snapshot : listVmSnapshots) {
try {
VMSnapshotStrategy strategy = findVMSnapshotStrategy(snapshot);
if (! strategy.deleteVMSnapshotFromDB(snapshot)) {
s_logger.error("Couldn't delete vm snapshot with id " + snapshot.getId());
return false;
}
}
catch (CloudRuntimeException e) {
s_logger.error("Couldn't delete vm snapshot due to: " + e.getMessage());
}
}
return true;
}
} }

View File

@ -21,6 +21,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.anyBoolean;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -118,7 +119,7 @@ public class AccountManagerImplVolumeDeleteEventTest extends AccountManagetImplT
VirtualMachineEntity vmEntity = mock(VirtualMachineEntity.class); VirtualMachineEntity vmEntity = mock(VirtualMachineEntity.class);
when(_orchSrvc.getVirtualMachine(anyString())).thenReturn(vmEntity); when(_orchSrvc.getVirtualMachine(anyString())).thenReturn(vmEntity);
when(vmEntity.destroy(anyString())).thenReturn(true); when(vmEntity.destroy(anyString(), anyBoolean())).thenReturn(true);
Mockito.doReturn(vm).when(_vmDao).findById(anyLong()); Mockito.doReturn(vm).when(_vmDao).findById(anyLong());