mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-11-04 00:02:37 +01:00 
			
		
		
		
	CLOUDSTACK-667: VM's base image update facility
This commit is contained in:
		
							parent
							
								
									59db01ce6e
								
							
						
					
					
						commit
						6c01b62cdc
					
				@ -22,6 +22,7 @@ import org.apache.cloudstack.api.ApiErrorCode;
 | 
			
		||||
import org.apache.cloudstack.api.BaseAsyncCmd;
 | 
			
		||||
import org.apache.cloudstack.api.Parameter;
 | 
			
		||||
import org.apache.cloudstack.api.ServerApiException;
 | 
			
		||||
import org.apache.cloudstack.api.response.TemplateResponse;
 | 
			
		||||
import org.apache.cloudstack.api.response.UserVmResponse;
 | 
			
		||||
import org.apache.log4j.Logger;
 | 
			
		||||
 | 
			
		||||
@ -34,7 +35,7 @@ import com.cloud.user.Account;
 | 
			
		||||
import com.cloud.user.UserContext;
 | 
			
		||||
import com.cloud.uservm.UserVm;
 | 
			
		||||
 | 
			
		||||
@APICommand(name = "restoreVirtualMachine", description="Restore a VM to original template or specific snapshot", responseObject=UserVmResponse.class, since="3.0.0")
 | 
			
		||||
@APICommand(name = "restoreVirtualMachine", description="Restore a VM to original template or new template", responseObject=UserVmResponse.class, since="3.0.0")
 | 
			
		||||
public class RestoreVMCmd extends BaseAsyncCmd {
 | 
			
		||||
    public static final Logger s_logger = Logger.getLogger(RestoreVMCmd.class);
 | 
			
		||||
    private static final String s_name = "restorevmresponse";
 | 
			
		||||
@ -43,6 +44,9 @@ public class RestoreVMCmd extends BaseAsyncCmd {
 | 
			
		||||
            required=true, description="Virtual Machine ID")
 | 
			
		||||
    private Long vmId;
 | 
			
		||||
 | 
			
		||||
    @Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.UUID, entityType = TemplateResponse.class, description="an optional template Id to restore vm from the new template")
 | 
			
		||||
    private Long templateId;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getEventType() {
 | 
			
		||||
        return EventTypes.EVENT_VM_RESTORE;
 | 
			
		||||
@ -85,4 +89,8 @@ public class RestoreVMCmd extends BaseAsyncCmd {
 | 
			
		||||
    public long getVmId() {
 | 
			
		||||
        return vmId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Long getTemplateId() {
 | 
			
		||||
        return templateId;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2675,8 +2675,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 | 
			
		||||
        // If the VM is Volatile in nature, on reboot discard the VM's root disk and create a new root disk for it: by calling restoreVM
 | 
			
		||||
        long serviceOfferingId = vmInstance.getServiceOfferingId();
 | 
			
		||||
        ServiceOfferingVO offering = _serviceOfferingDao.findById(serviceOfferingId);
 | 
			
		||||
        if(offering.getVolatileVm()){
 | 
			
		||||
            return restoreVMInternal(caller, vmInstance);
 | 
			
		||||
        if(offering != null && offering.getRemoved() == null) {
 | 
			
		||||
            if(offering.getVolatileVm()){
 | 
			
		||||
                return restoreVMInternal(caller, vmInstance, null);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId + " corresponding to the vm");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return rebootVirtualMachine(UserContext.current().getCallerUserId(),
 | 
			
		||||
@ -4795,9 +4799,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 | 
			
		||||
    public UserVm restoreVM(RestoreVMCmd cmd) {
 | 
			
		||||
        // Input validation
 | 
			
		||||
        Account caller = UserContext.current().getCaller();
 | 
			
		||||
        Long userId = UserContext.current().getCallerUserId();
 | 
			
		||||
 | 
			
		||||
        long vmId = cmd.getVmId();
 | 
			
		||||
        Long newTemplateId = cmd.getTemplateId();
 | 
			
		||||
        UserVmVO vm = _vmDao.findById(vmId);
 | 
			
		||||
        if (vm == null) {
 | 
			
		||||
            InvalidParameterValueException ex = new InvalidParameterValueException("Cannot find VM with ID " + vmId);
 | 
			
		||||
@ -4805,10 +4809,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 | 
			
		||||
            throw ex;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return restoreVMInternal(caller, vm);
 | 
			
		||||
        _accountMgr.checkAccess(caller, null, true, vm);
 | 
			
		||||
 | 
			
		||||
        return restoreVMInternal(caller, vm, newTemplateId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public UserVm restoreVMInternal(Account caller, UserVmVO vm){
 | 
			
		||||
    public UserVm restoreVMInternal(Account caller, UserVmVO vm, Long newTemplateId){
 | 
			
		||||
 | 
			
		||||
        Long userId = caller.getId();
 | 
			
		||||
        Account owner = _accountDao.findById(vm.getAccountId());
 | 
			
		||||
@ -4857,13 +4863,19 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 | 
			
		||||
            throw ex;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        VMTemplateVO template = _templateDao.findById(templateId);
 | 
			
		||||
        if (template == null) {
 | 
			
		||||
            InvalidParameterValueException ex = new InvalidParameterValueException(
 | 
			
		||||
                    "Cannot find template for specified volumeid and vmId");
 | 
			
		||||
            ex.addProxyObject(vm, vmId, "vmId");
 | 
			
		||||
            ex.addProxyObject(root, root.getId(), "volumeId");
 | 
			
		||||
            throw ex;
 | 
			
		||||
        VMTemplateVO template = null;
 | 
			
		||||
        if(newTemplateId != null) {
 | 
			
		||||
            template = _templateDao.findById(newTemplateId);
 | 
			
		||||
            _accountMgr.checkAccess(caller, null, true, template);
 | 
			
		||||
        } else {
 | 
			
		||||
            template = _templateDao.findById(templateId);
 | 
			
		||||
            if (template == null) {
 | 
			
		||||
                InvalidParameterValueException ex = new InvalidParameterValueException(
 | 
			
		||||
                        "Cannot find template for specified volumeid and vmId");
 | 
			
		||||
                ex.addProxyObject(vm, vmId, "vmId");
 | 
			
		||||
                ex.addProxyObject(root, root.getId(), "volumeId");
 | 
			
		||||
                throw ex;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (needRestart) {
 | 
			
		||||
@ -4878,8 +4890,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* allocate a new volume from original template */
 | 
			
		||||
        VolumeVO newVol = _storageMgr.allocateDuplicateVolume(root, null);
 | 
			
		||||
        /* If new template is provided allocate a new volume from new template otherwise allocate new volume from original template */
 | 
			
		||||
        VolumeVO newVol = null;
 | 
			
		||||
        if (newTemplateId != null){
 | 
			
		||||
            newVol = _storageMgr.allocateDuplicateVolume(root, newTemplateId);
 | 
			
		||||
            vm.setGuestOSId(template.getGuestOSId());
 | 
			
		||||
            vm.setTemplateId(newTemplateId);
 | 
			
		||||
            _vmDao.update(vmId, vm);
 | 
			
		||||
        } else newVol = _storageMgr.allocateDuplicateVolume(root, null);
 | 
			
		||||
 | 
			
		||||
        _volsDao.attachVolume(newVol.getId(), vmId, newVol.getDeviceId());
 | 
			
		||||
 | 
			
		||||
        /* Detach and destory the old root volume */
 | 
			
		||||
@ -4903,8 +4922,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        s_logger.debug("Restore VM " + vm.getUuid() + " with template "
 | 
			
		||||
                + root.getTemplateId() + " successfully");
 | 
			
		||||
        s_logger.debug("Restore VM " + vmId + " with template "
 | 
			
		||||
                + template.getUuid() + " done successfully");
 | 
			
		||||
        return vm;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -54,20 +54,20 @@ public class UserVmManagerTest {
 | 
			
		||||
    @Spy UserVmManagerImpl _userVmMgr = new UserVmManagerImpl();
 | 
			
		||||
    @Mock VirtualMachineManager _itMgr;
 | 
			
		||||
    @Mock StorageManager _storageMgr;
 | 
			
		||||
    @Mock Account account;
 | 
			
		||||
    @Mock Account _account;
 | 
			
		||||
    @Mock AccountManager _accountMgr;
 | 
			
		||||
    @Mock AccountDao _accountDao;
 | 
			
		||||
    @Mock UserDao _userDao;
 | 
			
		||||
    @Mock UserVmDao _vmDao;
 | 
			
		||||
    @Mock VMTemplateDao _templateDao;
 | 
			
		||||
    @Mock VolumeDao _volsDao;
 | 
			
		||||
    @Mock RestoreVMCmd restoreVMCmd;
 | 
			
		||||
    @Mock AccountVO accountMock;
 | 
			
		||||
    @Mock UserVO userMock;
 | 
			
		||||
    @Mock UserVmVO vmMock;
 | 
			
		||||
    @Mock VMTemplateVO templateMock;
 | 
			
		||||
    @Mock VolumeVO volumeMock;
 | 
			
		||||
    @Mock List<VolumeVO> rootVols;
 | 
			
		||||
    @Mock RestoreVMCmd _restoreVMCmd;
 | 
			
		||||
    @Mock AccountVO _accountMock;
 | 
			
		||||
    @Mock UserVO _userMock;
 | 
			
		||||
    @Mock UserVmVO _vmMock;
 | 
			
		||||
    @Mock VMTemplateVO _templateMock;
 | 
			
		||||
    @Mock VolumeVO _volumeMock;
 | 
			
		||||
    @Mock List<VolumeVO> _rootVols;
 | 
			
		||||
    @Before
 | 
			
		||||
    public void setup(){
 | 
			
		||||
        MockitoAnnotations.initMocks(this);
 | 
			
		||||
@ -79,69 +79,102 @@ public class UserVmManagerTest {
 | 
			
		||||
        _userVmMgr._storageMgr = _storageMgr;
 | 
			
		||||
        _userVmMgr._accountDao = _accountDao;
 | 
			
		||||
        _userVmMgr._userDao = _userDao;
 | 
			
		||||
        _userVmMgr._accountMgr = _accountMgr;
 | 
			
		||||
 | 
			
		||||
        doReturn(3L).when(account).getId();
 | 
			
		||||
        doReturn(8L).when(vmMock).getAccountId();
 | 
			
		||||
        when(_accountDao.findById(anyLong())).thenReturn(accountMock);
 | 
			
		||||
        when(_userDao.findById(anyLong())).thenReturn(userMock);
 | 
			
		||||
        doReturn(Account.State.enabled).when(account).getState();
 | 
			
		||||
        when(vmMock.getId()).thenReturn(314L);
 | 
			
		||||
        doReturn(3L).when(_account).getId();
 | 
			
		||||
        doReturn(8L).when(_vmMock).getAccountId();
 | 
			
		||||
        when(_accountDao.findById(anyLong())).thenReturn(_accountMock);
 | 
			
		||||
        when(_userDao.findById(anyLong())).thenReturn(_userMock);
 | 
			
		||||
        doReturn(Account.State.enabled).when(_account).getState();
 | 
			
		||||
        when(_vmMock.getId()).thenReturn(314L);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // VM state not in running/stopped case
 | 
			
		||||
    // Test restoreVm when VM state not in running/stopped case
 | 
			
		||||
    @Test(expected=CloudRuntimeException.class)
 | 
			
		||||
    public void testRestoreVMF1() throws ResourceAllocationException {
 | 
			
		||||
 | 
			
		||||
        when(_vmDao.findById(anyLong())).thenReturn(vmMock);
 | 
			
		||||
        when(_templateDao.findById(anyLong())).thenReturn(templateMock);
 | 
			
		||||
        doReturn(VirtualMachine.State.Error).when(vmMock).getState();
 | 
			
		||||
        _userVmMgr.restoreVMInternal(account, vmMock);
 | 
			
		||||
        when(_vmDao.findById(anyLong())).thenReturn(_vmMock);
 | 
			
		||||
        when(_templateDao.findById(anyLong())).thenReturn(_templateMock);
 | 
			
		||||
        doReturn(VirtualMachine.State.Error).when(_vmMock).getState();
 | 
			
		||||
        _userVmMgr.restoreVMInternal(_account, _vmMock, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // when VM is in stopped state
 | 
			
		||||
    // Test restoreVm when VM is in stopped state
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testRestoreVMF2()  throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
 | 
			
		||||
    ConcurrentOperationException, ResourceAllocationException {
 | 
			
		||||
        ConcurrentOperationException, ResourceAllocationException {
 | 
			
		||||
 | 
			
		||||
        doReturn(VirtualMachine.State.Stopped).when(vmMock).getState();
 | 
			
		||||
        when(_vmDao.findById(anyLong())).thenReturn(vmMock);
 | 
			
		||||
        when(_volsDao.findByInstance(anyLong())).thenReturn(rootVols);
 | 
			
		||||
        doReturn(false).when(rootVols).isEmpty();
 | 
			
		||||
        when(rootVols.get(eq(0))).thenReturn(volumeMock);
 | 
			
		||||
        doReturn(3L).when(volumeMock).getTemplateId();
 | 
			
		||||
        when(_templateDao.findById(anyLong())).thenReturn(templateMock);
 | 
			
		||||
        when(_storageMgr.allocateDuplicateVolume(volumeMock, null)).thenReturn(volumeMock);
 | 
			
		||||
        doReturn(VirtualMachine.State.Stopped).when(_vmMock).getState();
 | 
			
		||||
        when(_vmDao.findById(anyLong())).thenReturn(_vmMock);
 | 
			
		||||
        when(_volsDao.findByInstance(anyLong())).thenReturn(_rootVols);
 | 
			
		||||
        doReturn(false).when(_rootVols).isEmpty();
 | 
			
		||||
        when(_rootVols.get(eq(0))).thenReturn(_volumeMock);
 | 
			
		||||
        doReturn(3L).when(_volumeMock).getTemplateId();
 | 
			
		||||
        when(_templateDao.findById(anyLong())).thenReturn(_templateMock);
 | 
			
		||||
        when(_storageMgr.allocateDuplicateVolume(_volumeMock, null)).thenReturn(_volumeMock);
 | 
			
		||||
        doNothing().when(_volsDao).attachVolume(anyLong(), anyLong(), anyLong());
 | 
			
		||||
        when(volumeMock.getId()).thenReturn(3L);
 | 
			
		||||
        when(_volumeMock.getId()).thenReturn(3L);
 | 
			
		||||
        doNothing().when(_volsDao).detachVolume(anyLong());
 | 
			
		||||
        when(_storageMgr.destroyVolume(volumeMock)).thenReturn(true);
 | 
			
		||||
        when(_storageMgr.destroyVolume(_volumeMock)).thenReturn(true);
 | 
			
		||||
        when(_templateMock.getUuid()).thenReturn("e0552266-7060-11e2-bbaa-d55f5db67735");
 | 
			
		||||
 | 
			
		||||
        _userVmMgr.restoreVMInternal(account, vmMock);
 | 
			
		||||
        _userVmMgr.restoreVMInternal(_account, _vmMock, null);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // when VM is in running state
 | 
			
		||||
    // Test restoreVM when VM is in running state
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testRestoreVMF3()  throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
 | 
			
		||||
    ConcurrentOperationException, ResourceAllocationException {
 | 
			
		||||
        ConcurrentOperationException, ResourceAllocationException {
 | 
			
		||||
 | 
			
		||||
        doReturn(VirtualMachine.State.Running).when(vmMock).getState();
 | 
			
		||||
        when(_vmDao.findById(anyLong())).thenReturn(vmMock);
 | 
			
		||||
        when(_volsDao.findByInstance(anyLong())).thenReturn(rootVols);
 | 
			
		||||
        doReturn(false).when(rootVols).isEmpty();
 | 
			
		||||
        when(rootVols.get(eq(0))).thenReturn(volumeMock);
 | 
			
		||||
        doReturn(3L).when(volumeMock).getTemplateId();
 | 
			
		||||
        when(_templateDao.findById(anyLong())).thenReturn(templateMock);
 | 
			
		||||
        when(_itMgr.stop(vmMock, userMock, account)).thenReturn(true);
 | 
			
		||||
        when(_itMgr.start(vmMock, null, userMock, account)).thenReturn(vmMock);
 | 
			
		||||
        when(_storageMgr.allocateDuplicateVolume(volumeMock, null)).thenReturn(volumeMock);
 | 
			
		||||
        doReturn(VirtualMachine.State.Running).when(_vmMock).getState();
 | 
			
		||||
        when(_vmDao.findById(anyLong())).thenReturn(_vmMock);
 | 
			
		||||
        when(_volsDao.findByInstance(anyLong())).thenReturn(_rootVols);
 | 
			
		||||
        doReturn(false).when(_rootVols).isEmpty();
 | 
			
		||||
        when(_rootVols.get(eq(0))).thenReturn(_volumeMock);
 | 
			
		||||
        doReturn(3L).when(_volumeMock).getTemplateId();
 | 
			
		||||
        when(_templateDao.findById(anyLong())).thenReturn(_templateMock);
 | 
			
		||||
        when(_itMgr.stop(_vmMock, _userMock, _account)).thenReturn(true);
 | 
			
		||||
        when(_itMgr.start(_vmMock, null, _userMock, _account)).thenReturn(_vmMock);
 | 
			
		||||
        when(_storageMgr.allocateDuplicateVolume(_volumeMock, null)).thenReturn(_volumeMock);
 | 
			
		||||
        doNothing().when(_volsDao).attachVolume(anyLong(), anyLong(), anyLong());
 | 
			
		||||
        when(volumeMock.getId()).thenReturn(3L);
 | 
			
		||||
        when(_volumeMock.getId()).thenReturn(3L);
 | 
			
		||||
        doNothing().when(_volsDao).detachVolume(anyLong());
 | 
			
		||||
        when(_storageMgr.destroyVolume(volumeMock)).thenReturn(true);
 | 
			
		||||
        when(_storageMgr.destroyVolume(_volumeMock)).thenReturn(true);
 | 
			
		||||
        when(_templateMock.getUuid()).thenReturn("e0552266-7060-11e2-bbaa-d55f5db67735");
 | 
			
		||||
 | 
			
		||||
        _userVmMgr.restoreVMInternal(account, vmMock);
 | 
			
		||||
        _userVmMgr.restoreVMInternal(_account, _vmMock, null);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Test restoreVM on providing new template Id, when VM is in running state
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testRestoreVMF4()  throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
 | 
			
		||||
            ConcurrentOperationException, ResourceAllocationException {
 | 
			
		||||
        doReturn(VirtualMachine.State.Running).when(_vmMock).getState();
 | 
			
		||||
        when(_vmDao.findById(anyLong())).thenReturn(_vmMock);
 | 
			
		||||
        when(_volsDao.findByInstance(anyLong())).thenReturn(_rootVols);
 | 
			
		||||
        doReturn(false).when(_rootVols).isEmpty();
 | 
			
		||||
        when(_rootVols.get(eq(0))).thenReturn(_volumeMock);
 | 
			
		||||
        doReturn(3L).when(_volumeMock).getTemplateId();
 | 
			
		||||
        when(_templateDao.findById(anyLong())).thenReturn(_templateMock);
 | 
			
		||||
        doNothing().when(_accountMgr).checkAccess(_account, null, true, _templateMock);
 | 
			
		||||
        when(_itMgr.stop(_vmMock, _userMock, _account)).thenReturn(true);
 | 
			
		||||
        when(_storageMgr.allocateDuplicateVolume(_volumeMock, 14L)).thenReturn(_volumeMock);
 | 
			
		||||
        when(_templateMock.getGuestOSId()).thenReturn(5L);
 | 
			
		||||
        doNothing().when(_vmMock).setGuestOSId(anyLong());
 | 
			
		||||
        doNothing().when(_vmMock).setTemplateId(3L);
 | 
			
		||||
        when(_vmDao.update(314L, _vmMock)).thenReturn(true);
 | 
			
		||||
        when(_itMgr.start(_vmMock, null, _userMock, _account)).thenReturn(_vmMock);
 | 
			
		||||
        when(_storageMgr.allocateDuplicateVolume(_volumeMock, null)).thenReturn(_volumeMock);
 | 
			
		||||
        doNothing().when(_volsDao).attachVolume(anyLong(), anyLong(), anyLong());
 | 
			
		||||
        when(_volumeMock.getId()).thenReturn(3L);
 | 
			
		||||
        doNothing().when(_volsDao).detachVolume(anyLong());
 | 
			
		||||
        when(_storageMgr.destroyVolume(_volumeMock)).thenReturn(true);
 | 
			
		||||
        when(_templateMock.getUuid()).thenReturn("b1a3626e-72e0-4697-8c7c-a110940cc55d");
 | 
			
		||||
 | 
			
		||||
        _userVmMgr.restoreVMInternal(_account, _vmMock, 14L);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user