CLOUDSTACK-667: VM's base image update facility
Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/6c01b62c Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/6c01b62c Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/6c01b62c Branch: refs/heads/storage_refactor Commit: 6c01b62cdc2fe068d50b4e37739721dbc722cc41 Parents: 59db01c Author: Harikrishna Patnala <[email protected]> Authored: Thu Feb 21 15:03:18 2013 +0530 Committer: Abhinandan Prateek <[email protected]> Committed: Thu Feb 21 15:04:29 2013 +0530 ---------------------------------------------------------------------- .../api/command/user/vm/RestoreVMCmd.java | 10 +- server/src/com/cloud/vm/UserVmManagerImpl.java | 51 ++++-- server/test/com/cloud/vm/UserVmManagerTest.java | 131 +++++++++------ 3 files changed, 126 insertions(+), 66 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/6c01b62c/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java index e98c2f2..9c33f97 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java @@ -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; + } } http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/6c01b62c/server/src/com/cloud/vm/UserVmManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index ea25c66..ed8cd36 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -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; } http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/6c01b62c/server/test/com/cloud/vm/UserVmManagerTest.java ---------------------------------------------------------------------- diff --git a/server/test/com/cloud/vm/UserVmManagerTest.java b/server/test/com/cloud/vm/UserVmManagerTest.java index 46069ed..07cad47 100755 --- a/server/test/com/cloud/vm/UserVmManagerTest.java +++ b/server/test/com/cloud/vm/UserVmManagerTest.java @@ -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 { - - 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); + 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); 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 { - - 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); + 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); 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); }
