mirror of https://github.com/apache/cloudstack.git
Merge pull request #1727 from nvazquez/change-serv-offering
CLOUDSTACK-9539: Support changing Service offering for instance with VM Snapshots## Actual behaviour CloudStack doesn't support changing service offering for vm instances which have vm snapshots, they should be removed before changing service offering. ## Goal Extend actual behaviour by supporting changing service offering for vms which have vm snapshots. In that case, previously taken snapshots (if reverted) should use previous service offering, future snapshots should use the newest. ## Proposed solution: 1. Adding `service_offering_id` column on `vm_snapshots` table: This way snapshot can be reverted to original state even though service offering can be changed for vm instance. NOTE: Existing vm snapshots are populated on update script by: `UPDATE vm_snapshots s JOIN vm_instance v ON v.id = s.vm_id SET s.service_offering_id = v.service_offering_id;` 2. New vm snapshots will use instance vm service offering id as `service_offering_id` 3. Revert to vm snapshots should use vm snapshot's `service_offering_id` value. ## Example use case: - Deploy vm using service offering A - Take vm snapshot -> snap1 (service offering A) - Stop vm - Change vm service offering to B - Revert to VM snapshot snap 1 - Start vm It is expected that vm has service offering A after last step * pr/1727: CLOUDSTACK-9539: Support changing Service offering for instance with VM Snapshots Signed-off-by: Rajani Karuturi <rajani.karuturi@accelerite.com>
This commit is contained in:
commit
12497d04bf
|
|
@ -102,4 +102,6 @@ public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity
|
|||
|
||||
@Override
|
||||
public long getAccountId();
|
||||
|
||||
public long getServiceOfferingId();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,9 @@ public class VMSnapshotVO implements VMSnapshot {
|
|||
@Column(name = "domain_id")
|
||||
long domainId;
|
||||
|
||||
@Column(name = "service_offering_id")
|
||||
private long serviceOfferingId;
|
||||
|
||||
@Column(name = "vm_snapshot_type")
|
||||
@Enumerated(EnumType.STRING)
|
||||
VMSnapshot.Type type;
|
||||
|
|
@ -139,6 +142,7 @@ public class VMSnapshotVO implements VMSnapshot {
|
|||
displayName = vsDisplayName;
|
||||
this.type = type;
|
||||
this.current = current;
|
||||
this.serviceOfferingId = serviceOfferingId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -248,4 +252,9 @@ public class VMSnapshotVO implements VMSnapshot {
|
|||
public Class<?> getEntityType() {
|
||||
return VMSnapshot.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getServiceOfferingId() {
|
||||
return serviceOfferingId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -950,12 +950,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
+ "; make sure the virtual machine is stopped");
|
||||
}
|
||||
|
||||
// If target VM has associated VM snapshots then don't allow upgrading of VM
|
||||
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
|
||||
if (vmSnapshots.size() > 0) {
|
||||
throw new InvalidParameterValueException("Unable to change service offering for VM, please remove VM snapshots before changing service offering of VM");
|
||||
}
|
||||
|
||||
_accountMgr.checkAccess(caller, null, true, vmInstance);
|
||||
|
||||
// Check resource limits for CPU and Memory.
|
||||
|
|
@ -1616,11 +1610,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
|
||||
|
||||
if (vmInstance != null) {
|
||||
// If target VM has associated VM snapshots then don't allow upgrading of VM
|
||||
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
|
||||
if (vmSnapshots.size() > 0) {
|
||||
throw new InvalidParameterValueException("Unable to scale VM, please remove VM snapshots before scaling VM");
|
||||
}
|
||||
if (vmInstance.getState().equals(State.Stopped)) {
|
||||
upgradeStoppedVirtualMachine(vmId, newServiceOfferingId, customParameters);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -57,12 +57,16 @@ import com.cloud.event.EventTypes;
|
|||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.ManagementServerException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.exception.VirtualMachineMigrationException;
|
||||
import com.cloud.gpu.GPU;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
|
||||
import com.cloud.projects.Project.ListProjectResourcesCriteria;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.service.dao.ServiceOfferingDetailsDao;
|
||||
import com.cloud.storage.GuestOSVO;
|
||||
import com.cloud.storage.Snapshot;
|
||||
|
|
@ -89,7 +93,13 @@ import com.cloud.utils.db.EntityManager;
|
|||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallbackWithException;
|
||||
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
|
||||
import com.cloud.utils.db.TransactionStatus;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.UserVmDetailVO;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
|
|
@ -102,8 +112,10 @@ import com.cloud.vm.VmWorkJobHandler;
|
|||
import com.cloud.vm.VmWorkJobHandlerProxy;
|
||||
import com.cloud.vm.VmWorkSerializer;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.UserVmDetailsDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDetailsDao;
|
||||
|
||||
@Component
|
||||
public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase implements VMSnapshotManager, VMSnapshotService, VmWorkJobHandler {
|
||||
|
|
@ -135,6 +147,14 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
|||
|
||||
@Inject
|
||||
VmWorkJobDao _workJobDao;
|
||||
@Inject
|
||||
protected UserVmManager _userVmManager;
|
||||
@Inject
|
||||
protected ServiceOfferingDao _serviceOfferingDao;
|
||||
@Inject
|
||||
protected UserVmDetailsDao _userVmDetailsDao;
|
||||
@Inject
|
||||
protected VMSnapshotDetailsDao _vmSnapshotDetailsDao;
|
||||
|
||||
VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this);
|
||||
|
||||
|
|
@ -346,14 +366,7 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
|||
vmSnapshotType = VMSnapshot.Type.DiskAndMemory;
|
||||
|
||||
try {
|
||||
VMSnapshotVO vmSnapshotVo =
|
||||
new VMSnapshotVO(userVmVo.getAccountId(), userVmVo.getDomainId(), vmId, vsDescription, vmSnapshotName, vsDisplayName, userVmVo.getServiceOfferingId(),
|
||||
vmSnapshotType, null);
|
||||
VMSnapshot vmSnapshot = _vmSnapshotDao.persist(vmSnapshotVo);
|
||||
if (vmSnapshot == null) {
|
||||
throw new CloudRuntimeException("Failed to create snapshot for vm: " + vmId);
|
||||
}
|
||||
return vmSnapshot;
|
||||
return createAndPersistVMSnapshot(userVmVo, vsDescription, vmSnapshotName, vsDisplayName, vmSnapshotType);
|
||||
} catch (Exception e) {
|
||||
String msg = e.getMessage();
|
||||
s_logger.error("Create vm snapshot record failed for vm: " + vmId + " due to: " + msg);
|
||||
|
|
@ -361,6 +374,57 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create, persist and return vm snapshot for userVmVo with given parameters.
|
||||
* Persistence and support for custom service offerings are done on the same transaction
|
||||
* @param userVmVo user vm
|
||||
* @param vmId vm id
|
||||
* @param vsDescription vm description
|
||||
* @param vmSnapshotName vm snapshot name
|
||||
* @param vsDisplayName vm snapshot display name
|
||||
* @param vmSnapshotType vm snapshot type
|
||||
* @return vm snapshot
|
||||
* @throws CloudRuntimeException if vm snapshot couldn't be persisted
|
||||
*/
|
||||
protected VMSnapshot createAndPersistVMSnapshot(UserVmVO userVmVo, String vsDescription, String vmSnapshotName, String vsDisplayName, VMSnapshot.Type vmSnapshotType) {
|
||||
final Long vmId = userVmVo.getId();
|
||||
final Long serviceOfferingId = userVmVo.getServiceOfferingId();
|
||||
final VMSnapshotVO vmSnapshotVo =
|
||||
new VMSnapshotVO(userVmVo.getAccountId(), userVmVo.getDomainId(), vmId, vsDescription, vmSnapshotName, vsDisplayName, serviceOfferingId,
|
||||
vmSnapshotType, null);
|
||||
return Transaction.execute(new TransactionCallbackWithException<VMSnapshot, CloudRuntimeException>() {
|
||||
@Override
|
||||
public VMSnapshot doInTransaction(TransactionStatus status) {
|
||||
VMSnapshot vmSnapshot = _vmSnapshotDao.persist(vmSnapshotVo);
|
||||
if (vmSnapshot == null) {
|
||||
throw new CloudRuntimeException("Failed to create snapshot for vm: " + vmId);
|
||||
}
|
||||
addSupportForCustomServiceOffering(vmId, serviceOfferingId, vmSnapshot.getId());
|
||||
return vmSnapshot;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add entries on vm_snapshot_details if service offering is dynamic. This will allow setting details when revert to vm snapshot
|
||||
* @param vmId vm id
|
||||
* @param serviceOfferingId service offering id
|
||||
* @param vmSnapshotId vm snapshot id
|
||||
*/
|
||||
protected void addSupportForCustomServiceOffering(long vmId, long serviceOfferingId, long vmSnapshotId) {
|
||||
ServiceOfferingVO serviceOfferingVO = _serviceOfferingDao.findById(serviceOfferingId);
|
||||
if (serviceOfferingVO.isDynamic()) {
|
||||
List<UserVmDetailVO> vmDetails = _userVmDetailsDao.listDetails(vmId);
|
||||
List<VMSnapshotDetailsVO> vmSnapshotDetails = new ArrayList<VMSnapshotDetailsVO>();
|
||||
for (UserVmDetailVO detail : vmDetails) {
|
||||
if(detail.getName().equalsIgnoreCase("cpuNumber") || detail.getName().equalsIgnoreCase("cpuSpeed") || detail.getName().equalsIgnoreCase("memory")) {
|
||||
vmSnapshotDetails.add(new VMSnapshotDetailsVO(vmSnapshotId, detail.getName(), detail.getValue(), detail.isDisplay()));
|
||||
}
|
||||
}
|
||||
_vmSnapshotDetailsDao.saveDetails(vmSnapshotDetails);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return _name;
|
||||
|
|
@ -654,16 +718,76 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If snapshot was taken with a different service offering than actual used in vm, should change it back to it
|
||||
* @param userVm vm to change service offering (if necessary)
|
||||
* @param vmSnapshotVo vm snapshot
|
||||
*/
|
||||
protected void updateUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) {
|
||||
if (vmSnapshotVo.getServiceOfferingId() != userVm.getServiceOfferingId()) {
|
||||
changeUserVmServiceOffering(userVm, vmSnapshotVo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user vm details as a map
|
||||
* @param userVm user vm
|
||||
* @return map
|
||||
*/
|
||||
protected Map<String, String> getVmMapDetails(UserVm userVm) {
|
||||
List<UserVmDetailVO> userVmDetails = _userVmDetailsDao.listDetails(userVm.getId());
|
||||
Map<String, String> details = new HashMap<String, String>();
|
||||
for (UserVmDetailVO detail : userVmDetails) {
|
||||
details.put(detail.getName(), detail.getValue());
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update service offering on {@link userVm} to the one specified in {@link vmSnapshotVo}
|
||||
* @param userVm user vm to be updated
|
||||
* @param vmSnapshotVo vm snapshot
|
||||
*/
|
||||
protected void changeUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) {
|
||||
Map<String, String> vmDetails = getVmMapDetails(userVm);
|
||||
boolean result = upgradeUserVmServiceOffering(userVm.getId(), vmSnapshotVo.getServiceOfferingId(), vmDetails);
|
||||
if (! result){
|
||||
throw new CloudRuntimeException("VM Snapshot reverting failed due to vm service offering couldn't be changed to the one used when snapshot was taken");
|
||||
}
|
||||
s_logger.debug("Successfully changed service offering to " + vmSnapshotVo.getServiceOfferingId() + " for vm " + userVm.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade virtual machine {@linkplain vmId} to new service offering {@linkplain serviceOfferingId}
|
||||
* @param vmId vm id
|
||||
* @param serviceOfferingId service offering id
|
||||
* @param details vm details
|
||||
* @return if operation was successful
|
||||
*/
|
||||
protected boolean upgradeUserVmServiceOffering(Long vmId, Long serviceOfferingId, Map<String, String> details) {
|
||||
boolean result;
|
||||
try {
|
||||
result = _userVmManager.upgradeVirtualMachine(vmId, serviceOfferingId, details);
|
||||
if (! result){
|
||||
s_logger.error("Couldn't change service offering for vm " + vmId + " to " + serviceOfferingId);
|
||||
}
|
||||
return result;
|
||||
} catch (ConcurrentOperationException | ResourceUnavailableException | ManagementServerException | VirtualMachineMigrationException e) {
|
||||
s_logger.error("Couldn't change service offering for vm " + vmId + " to " + serviceOfferingId + " due to: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private UserVm orchestrateRevertToVMSnapshot(Long vmSnapshotId) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException {
|
||||
|
||||
// check if VM snapshot exists in DB
|
||||
VMSnapshotVO vmSnapshotVo = _vmSnapshotDao.findById(vmSnapshotId);
|
||||
final VMSnapshotVO vmSnapshotVo = _vmSnapshotDao.findById(vmSnapshotId);
|
||||
if (vmSnapshotVo == null) {
|
||||
throw new InvalidParameterValueException(
|
||||
"unable to find the vm snapshot with id " + vmSnapshotId);
|
||||
}
|
||||
Long vmId = vmSnapshotVo.getVmId();
|
||||
UserVmVO userVm = _userVMDao.findById(vmId);
|
||||
final UserVmVO userVm = _userVMDao.findById(vmId);
|
||||
// check if VM exists
|
||||
if (userVm == null) {
|
||||
throw new InvalidParameterValueException("Revert vm to snapshot: "
|
||||
|
|
@ -721,6 +845,13 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
|||
try {
|
||||
VMSnapshotStrategy strategy = findVMSnapshotStrategy(vmSnapshotVo);
|
||||
strategy.revertVMSnapshot(vmSnapshotVo);
|
||||
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<CloudRuntimeException>() {
|
||||
@Override
|
||||
public void doInTransactionWithoutResult(TransactionStatus status) throws CloudRuntimeException {
|
||||
revertUserVmDetailsFromVmSnapshot(userVm, vmSnapshotVo);
|
||||
updateUserVmServiceOffering(userVm, vmSnapshotVo);
|
||||
}
|
||||
});
|
||||
return userVm;
|
||||
} catch (Exception e) {
|
||||
s_logger.debug("Failed to revert vmsnapshot: " + vmSnapshotId, e);
|
||||
|
|
@ -728,6 +859,23 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update or add user vm details from vm snapshot for vms with custom service offerings
|
||||
* @param userVm user vm
|
||||
* @param vmSnapshotVo vm snapshot
|
||||
*/
|
||||
protected void revertUserVmDetailsFromVmSnapshot(UserVmVO userVm, VMSnapshotVO vmSnapshotVo) {
|
||||
ServiceOfferingVO serviceOfferingVO = _serviceOfferingDao.findById(vmSnapshotVo.getServiceOfferingId());
|
||||
if (serviceOfferingVO.isDynamic()) {
|
||||
List<VMSnapshotDetailsVO> vmSnapshotDetails = _vmSnapshotDetailsDao.listDetails(vmSnapshotVo.getId());
|
||||
List<UserVmDetailVO> userVmDetails = new ArrayList<UserVmDetailVO>();
|
||||
for (VMSnapshotDetailsVO detail : vmSnapshotDetails) {
|
||||
userVmDetails.add(new UserVmDetailVO(userVm.getId(), detail.getName(), detail.getValue(), detail.isDisplay()));
|
||||
}
|
||||
_userVmDetailsDao.saveDetails(userVmDetails);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestoreVMSnapshotCommand createRestoreCommand(UserVmVO userVm, List<VMSnapshotVO> vmSnapshotVOs) {
|
||||
if (!HypervisorType.KVM.equals(userVm.getHypervisorType()))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
// under the License.
|
||||
package com.cloud.vm.snapshot;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyLong;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
|
|
@ -23,30 +24,45 @@ import static org.mockito.Mockito.doNothing;
|
|||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.ResourceDetail;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.ManagementServerException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.exception.VirtualMachineMigrationException;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.HypervisorGuruManager;
|
||||
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.service.dao.ServiceOfferingDetailsDao;
|
||||
import com.cloud.storage.GuestOSVO;
|
||||
import com.cloud.storage.Snapshot;
|
||||
|
|
@ -59,14 +75,19 @@ import com.cloud.user.Account;
|
|||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.user.dao.UserDao;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
import com.cloud.vm.UserVmDetailVO;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VirtualMachine.State;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.UserVmDetailsDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDetailsDao;
|
||||
|
||||
public class VMSnapshotManagerTest {
|
||||
@Spy
|
||||
|
|
@ -107,13 +128,52 @@ public class VMSnapshotManagerTest {
|
|||
HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
|
||||
@Mock
|
||||
ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
|
||||
@Mock
|
||||
ServiceOfferingDao _serviceOfferingDao;
|
||||
@Mock
|
||||
UserVmDetailsDao _userVmDetailsDao;
|
||||
@Mock
|
||||
VMSnapshotDetailsDao _vmSnapshotDetailsDao;
|
||||
@Mock
|
||||
UserVmManager _userVmManager;
|
||||
int _vmSnapshotMax = 10;
|
||||
|
||||
private static final long TEST_VM_ID = 3L;
|
||||
private static final long SERVICE_OFFERING_ID = 1L;
|
||||
private static final long SERVICE_OFFERING_DIFFERENT_ID = 2L;
|
||||
private static VMSnapshot.Type vmSnapshotType;
|
||||
private static List<UserVmDetailVO> userVmDetails;
|
||||
private static List<VMSnapshotDetailsVO> vmSnapshotDetails;
|
||||
|
||||
private static final long VM_SNAPSHOT_ID = 1L;
|
||||
private static final String VM_SNAPSHOT_NAME = "Vm-Snapshot-Name";
|
||||
private static final String VM_SNAPSHOT_DESCRIPTION = "Vm-Snapshot-Desc";
|
||||
private static final String VM_SNAPSHOT_DISPLAY_NAME = "Vm-Snapshot-Display-Name";
|
||||
@Mock
|
||||
UserVmVO vmMock;
|
||||
@Mock
|
||||
VolumeVO volumeMock;
|
||||
@Mock
|
||||
VMSnapshotVO vmSnapshotVO;
|
||||
@Mock
|
||||
ServiceOfferingVO serviceOffering;
|
||||
@Mock
|
||||
UserVmDetailVO userVmDetailCpuNumber;
|
||||
@Mock
|
||||
UserVmDetailVO userVmDetailMemory;
|
||||
@Mock
|
||||
VMSnapshotDetailsVO vmSnapshotDetailCpuNumber;
|
||||
@Mock
|
||||
VMSnapshotDetailsVO vmSnapshotDetailMemory;
|
||||
@Mock
|
||||
UserVm userVm;
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<List<VMSnapshotDetailsVO>> listVmSnapshotDetailsCaptor;
|
||||
@Captor
|
||||
ArgumentCaptor<Map<String,String>> mapDetailsCaptor;
|
||||
@Captor
|
||||
ArgumentCaptor<List<UserVmDetailVO>> listUserVmDetailsCaptor;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
|
@ -133,6 +193,11 @@ public class VMSnapshotManagerTest {
|
|||
|
||||
_vmSnapshotMgr._vmSnapshotMax = _vmSnapshotMax;
|
||||
|
||||
_vmSnapshotMgr._serviceOfferingDao = _serviceOfferingDao;
|
||||
_vmSnapshotMgr._userVmDetailsDao = _userVmDetailsDao;
|
||||
_vmSnapshotMgr._vmSnapshotDetailsDao = _vmSnapshotDetailsDao;
|
||||
_vmSnapshotMgr._userVmManager = _userVmManager;
|
||||
|
||||
when(_userVMDao.findById(anyLong())).thenReturn(vmMock);
|
||||
when(_vmSnapshotDao.findByName(anyLong(), anyString())).thenReturn(null);
|
||||
when(_vmSnapshotDao.findByVm(anyLong())).thenReturn(new ArrayList<VMSnapshotVO>());
|
||||
|
|
@ -144,10 +209,40 @@ public class VMSnapshotManagerTest {
|
|||
when(volumeMock.getInstanceId()).thenReturn(TEST_VM_ID);
|
||||
when(_volumeDao.findByInstance(anyLong())).thenReturn(mockVolumeList);
|
||||
|
||||
when(vmMock.getId()).thenReturn(TEST_VM_ID);
|
||||
when(vmMock.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_ID);
|
||||
when(vmMock.getAccountId()).thenReturn(1L);
|
||||
when(vmMock.getDomainId()).thenReturn(1L);
|
||||
when(vmMock.getInstanceName()).thenReturn("i-3-VM-TEST");
|
||||
when(vmMock.getState()).thenReturn(State.Running);
|
||||
when(vmMock.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer);
|
||||
when(_guestOSDao.findById(anyLong())).thenReturn(mock(GuestOSVO.class));
|
||||
|
||||
when(vmSnapshotVO.getId()).thenReturn(VM_SNAPSHOT_ID);
|
||||
when(serviceOffering.isDynamic()).thenReturn(false);
|
||||
when(_serviceOfferingDao.findById(SERVICE_OFFERING_ID)).thenReturn(serviceOffering);
|
||||
|
||||
for (ResourceDetail detail : Arrays.asList(userVmDetailCpuNumber, vmSnapshotDetailCpuNumber)) {
|
||||
when(detail.getName()).thenReturn("cpuNumber");
|
||||
when(detail.getValue()).thenReturn("2");
|
||||
when(detail.isDisplay()).thenReturn(true);
|
||||
}
|
||||
|
||||
for (ResourceDetail detail : Arrays.asList(userVmDetailMemory, vmSnapshotDetailMemory)) {
|
||||
when(detail.getName()).thenReturn("memory");
|
||||
when(detail.getValue()).thenReturn("2048");
|
||||
when(detail.isDisplay()).thenReturn(true);
|
||||
}
|
||||
|
||||
userVmDetails = Arrays.asList(userVmDetailCpuNumber, userVmDetailMemory);
|
||||
vmSnapshotDetails = Arrays.asList(vmSnapshotDetailCpuNumber, vmSnapshotDetailMemory);
|
||||
when(_userVmDetailsDao.listDetails(TEST_VM_ID)).thenReturn(userVmDetails);
|
||||
when(_vmSnapshotDetailsDao.listDetails(VM_SNAPSHOT_ID)).thenReturn(vmSnapshotDetails);
|
||||
|
||||
when(userVm.getId()).thenReturn(TEST_VM_ID);
|
||||
when(userVm.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_ID);
|
||||
|
||||
when(vmSnapshotVO.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_ID);
|
||||
}
|
||||
|
||||
// vmId null case
|
||||
|
|
@ -207,4 +302,104 @@ public class VMSnapshotManagerTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateAndPersistVMSnapshot() {
|
||||
when(_vmSnapshotDao.persist(any(VMSnapshotVO.class))).thenReturn(vmSnapshotVO);
|
||||
_vmSnapshotMgr.createAndPersistVMSnapshot(vmMock, VM_SNAPSHOT_DESCRIPTION,
|
||||
VM_SNAPSHOT_NAME, VM_SNAPSHOT_DISPLAY_NAME, vmSnapshotType);
|
||||
|
||||
verify(_vmSnapshotMgr).addSupportForCustomServiceOffering(TEST_VM_ID, SERVICE_OFFERING_ID, VM_SNAPSHOT_ID);
|
||||
}
|
||||
|
||||
@Test(expected=CloudRuntimeException.class)
|
||||
public void testCreateAndPersistVMSnapshotNullVMSnapshot() {
|
||||
when(_vmSnapshotDao.persist(any(VMSnapshotVO.class))).thenReturn(null);
|
||||
_vmSnapshotMgr.createAndPersistVMSnapshot(vmMock, VM_SNAPSHOT_DESCRIPTION,
|
||||
VM_SNAPSHOT_NAME, VM_SNAPSHOT_DISPLAY_NAME, vmSnapshotType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSupportForCustomServiceOfferingNotDynamicServiceOffering() {
|
||||
_vmSnapshotMgr.addSupportForCustomServiceOffering(TEST_VM_ID, SERVICE_OFFERING_ID, VM_SNAPSHOT_ID);
|
||||
verify(_userVmDetailsDao, never()).listDetails(TEST_VM_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSupportForCustomServiceOfferingDynamicServiceOffering() {
|
||||
when(serviceOffering.isDynamic()).thenReturn(true);
|
||||
_vmSnapshotMgr.addSupportForCustomServiceOffering(TEST_VM_ID, SERVICE_OFFERING_ID, VM_SNAPSHOT_ID);
|
||||
|
||||
verify(_userVmDetailsDao).listDetails(TEST_VM_ID);
|
||||
verify(_vmSnapshotDetailsDao).saveDetails(listVmSnapshotDetailsCaptor.capture());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserVmServiceOfferingSameServiceOffering() {
|
||||
_vmSnapshotMgr.updateUserVmServiceOffering(userVm, vmSnapshotVO);
|
||||
verify(_vmSnapshotMgr, never()).changeUserVmServiceOffering(userVm, vmSnapshotVO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserVmServiceOfferingDifferentServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException {
|
||||
when(userVm.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_DIFFERENT_ID);
|
||||
when(_userVmManager.upgradeVirtualMachine(Matchers.eq(TEST_VM_ID), Matchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true);
|
||||
_vmSnapshotMgr.updateUserVmServiceOffering(userVm, vmSnapshotVO);
|
||||
|
||||
verify(_vmSnapshotMgr).changeUserVmServiceOffering(userVm, vmSnapshotVO);
|
||||
verify(_vmSnapshotMgr).getVmMapDetails(userVm);
|
||||
verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(Matchers.eq(TEST_VM_ID), Matchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetVmMapDetails() {
|
||||
Map<String, String> result = _vmSnapshotMgr.getVmMapDetails(userVm);
|
||||
assert(result.containsKey(userVmDetailCpuNumber.getName()));
|
||||
assert(result.containsKey(userVmDetailMemory.getName()));
|
||||
assertEquals(userVmDetails.size(), result.size());
|
||||
assertEquals(userVmDetailCpuNumber.getValue(), result.get(userVmDetailCpuNumber.getName()));
|
||||
assertEquals(userVmDetailMemory.getValue(), result.get(userVmDetailMemory.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeUserVmServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException {
|
||||
when(_userVmManager.upgradeVirtualMachine(Matchers.eq(TEST_VM_ID), Matchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true);
|
||||
_vmSnapshotMgr.changeUserVmServiceOffering(userVm, vmSnapshotVO);
|
||||
verify(_vmSnapshotMgr).getVmMapDetails(userVm);
|
||||
verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(Matchers.eq(TEST_VM_ID), Matchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture());
|
||||
}
|
||||
|
||||
@Test(expected=CloudRuntimeException.class)
|
||||
public void testChangeUserVmServiceOfferingFailOnUpgradeVMServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException {
|
||||
when(_userVmManager.upgradeVirtualMachine(Matchers.eq(TEST_VM_ID), Matchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(false);
|
||||
_vmSnapshotMgr.changeUserVmServiceOffering(userVm, vmSnapshotVO);
|
||||
verify(_vmSnapshotMgr).getVmMapDetails(userVm);
|
||||
verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(Matchers.eq(TEST_VM_ID), Matchers.eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpgradeUserVmServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException {
|
||||
Map<String, String> details = new HashMap<String, String>() {{
|
||||
put(userVmDetailCpuNumber.getName(), userVmDetailCpuNumber.getValue());
|
||||
put(userVmDetailMemory.getName(), userVmDetailMemory.getValue());
|
||||
}};
|
||||
when(_userVmManager.upgradeVirtualMachine(TEST_VM_ID, SERVICE_OFFERING_ID, details)).thenReturn(true);
|
||||
_vmSnapshotMgr.upgradeUserVmServiceOffering(TEST_VM_ID, SERVICE_OFFERING_ID, details);
|
||||
|
||||
verify(_userVmManager).upgradeVirtualMachine(TEST_VM_ID, SERVICE_OFFERING_ID, details);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevertUserVmDetailsFromVmSnapshotNotDynamicServiceOffering() {
|
||||
_vmSnapshotMgr.revertUserVmDetailsFromVmSnapshot(vmMock, vmSnapshotVO);
|
||||
verify(_vmSnapshotDetailsDao, never()).listDetails(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevertUserVmDetailsFromVmSnapshotDynamicServiceOffering() {
|
||||
when(serviceOffering.isDynamic()).thenReturn(true);
|
||||
_vmSnapshotMgr.revertUserVmDetailsFromVmSnapshot(vmMock, vmSnapshotVO);
|
||||
verify(_vmSnapshotDetailsDao).listDetails(VM_SNAPSHOT_ID);
|
||||
verify(_userVmDetailsDao).saveDetails(listUserVmDetailsCaptor.capture());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,3 +215,17 @@ VIEW `image_store_view` AS
|
|||
FROM
|
||||
(`image_store`
|
||||
LEFT JOIN `data_center` ON ((`image_store`.`data_center_id` = `data_center`.`id`)));
|
||||
|
||||
-- Add service_offering_id column to vm_snapshots table
|
||||
ALTER TABLE `cloud`.`vm_snapshots` ADD COLUMN `service_offering_id` BIGINT(20) UNSIGNED NOT NULL COMMENT '' AFTER `domain_id`;
|
||||
UPDATE `cloud`.`vm_snapshots` s JOIN `cloud`.`vm_instance` v ON v.id = s.vm_id SET s.service_offering_id = v.service_offering_id;
|
||||
ALTER TABLE `cloud`.`vm_snapshots` ADD CONSTRAINT `fk_vm_snapshots_service_offering_id` FOREIGN KEY (`service_offering_id`) REFERENCES `cloud`.`service_offering` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
|
||||
-- Update vm snapshot details for instances with custom service offerings
|
||||
INSERT INTO `cloud`.`vm_snapshot_details` (vm_snapshot_id, name, value)
|
||||
SELECT s.id, d.name, d.value
|
||||
FROM `cloud`.`user_vm_details` d JOIN `cloud`.`vm_instance` v ON (d.vm_id = v.id)
|
||||
JOIN `cloud`.`service_offering` o ON (v.service_offering_id = o.id)
|
||||
JOIN `cloud`.`vm_snapshots` s ON (s.service_offering_id = o.id AND s.vm_id = v.id)
|
||||
WHERE (o.cpu is null AND o.speed IS NULL AND o.ram_size IS NULL) AND
|
||||
(d.name = 'cpuNumber' OR d.name = 'cpuSpeed' OR d.name = 'memory');
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
# under the License.
|
||||
|
||||
# Import Local Modules
|
||||
from marvin.codes import FAILED, KVM, PASS, XEN_SERVER
|
||||
from marvin.codes import FAILED, KVM, PASS, XEN_SERVER, RUNNING
|
||||
from nose.plugins.attrib import attr
|
||||
from marvin.cloudstackTestCase import cloudstackTestCase
|
||||
from marvin.lib.utils import random_gen, cleanup_resources, validateList, is_snapshot_on_nfs
|
||||
from marvin.lib.utils import random_gen, cleanup_resources, validateList, is_snapshot_on_nfs, isAlmostEqual
|
||||
from marvin.lib.base import (Account,
|
||||
ServiceOffering,
|
||||
VirtualMachine,
|
||||
|
|
@ -29,7 +29,8 @@ from marvin.lib.base import (Account,
|
|||
from marvin.lib.common import (get_zone,
|
||||
get_domain,
|
||||
get_template,
|
||||
list_snapshots)
|
||||
list_snapshots,
|
||||
list_virtual_machines)
|
||||
import time
|
||||
|
||||
|
||||
|
|
@ -407,3 +408,256 @@ class TestSnapshots(cloudstackTestCase):
|
|||
volume_id=volume.id)
|
||||
|
||||
return
|
||||
|
||||
class Utils:
|
||||
|
||||
def __init__(self):
|
||||
self.added_service_offerings = {
|
||||
'testOffering1' : {'displaytext': 'Test Offering 1', 'cpuspeed': 600, 'cpunumber': 1, 'name': 'Test Offering 1', 'memory': 256},
|
||||
'testOffering2' : {'displaytext': 'Test Offering 2', 'cpuspeed': 600, 'cpunumber': 2, 'name': 'Test Offering 2', 'memory': 512}
|
||||
}
|
||||
|
||||
class TestChangeServiceOfferingForVmWithSnapshots(cloudstackTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
try:
|
||||
cls._cleanup = []
|
||||
cls.testClient = super(TestChangeServiceOfferingForVmWithSnapshots, cls).getClsTestClient()
|
||||
cls.api_client = cls.testClient.getApiClient()
|
||||
cls.services = cls.testClient.getParsedTestDataConfig()
|
||||
cls.hypervisor = cls.testClient.getHypervisorInfo()
|
||||
cls.unsupportedHypervisor = False
|
||||
if cls.hypervisor.lower() in (KVM.lower(), "hyperv", "lxc"):
|
||||
cls.unsupportedHypervisor = True
|
||||
return
|
||||
|
||||
cls.domain = get_domain(cls.api_client)
|
||||
cls.zone = get_zone(
|
||||
cls.api_client,
|
||||
cls.testClient.getZoneForTests()
|
||||
)
|
||||
cls.services["small"]["zoneid"] = cls.zone.id
|
||||
cls.template = get_template(
|
||||
cls.api_client,
|
||||
cls.zone.id,
|
||||
cls.services["ostype"]
|
||||
)
|
||||
if cls.template == FAILED:
|
||||
assert False, "get_template() failed to return template\
|
||||
with description %s" % cls.services["ostype"]
|
||||
|
||||
test_offerings = Utils().added_service_offerings
|
||||
for offering in test_offerings:
|
||||
cls.services["service_offerings"][offering] = test_offerings[offering]
|
||||
|
||||
# Create 2 different service offerings
|
||||
cls.service_offering_1 = ServiceOffering.create(
|
||||
cls.api_client,
|
||||
cls.services["service_offerings"]["testOffering1"]
|
||||
)
|
||||
cls._cleanup.append(cls.service_offering_1)
|
||||
|
||||
cls.service_offering_2 = ServiceOffering.create(
|
||||
cls.api_client,
|
||||
cls.services["service_offerings"]["testOffering2"]
|
||||
)
|
||||
cls._cleanup.append(cls.service_offering_2)
|
||||
|
||||
cls.account = Account.create(
|
||||
cls.api_client,
|
||||
cls.services["account"],
|
||||
domainid=cls.domain.id
|
||||
)
|
||||
cls._cleanup.append(cls.account)
|
||||
|
||||
except Exception as e:
|
||||
cls.tearDownClass()
|
||||
raise Exception("Warning: Exception in setup : %s" % e)
|
||||
return
|
||||
|
||||
def setUp(self):
|
||||
self.apiclient = self.testClient.getApiClient()
|
||||
self.dbclient = self.testClient.getDbConnection()
|
||||
self.cleanup = []
|
||||
|
||||
if self.unsupportedHypervisor:
|
||||
self.skipTest("Skipping test because unsupported hypervisor\
|
||||
%s" % self.hypervisor)
|
||||
|
||||
def tearDown(self):
|
||||
# Clean up, terminate the created resources
|
||||
cleanup_resources(self.apiclient, self.cleanup)
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
try:
|
||||
cleanup_resources(cls.api_client, cls._cleanup)
|
||||
except Exception as e:
|
||||
raise Exception("Warning: Exception during cleanup : %s" % e)
|
||||
|
||||
return
|
||||
|
||||
def wait_vm_start(self, apiclient, vmid, timeout, sleep):
|
||||
while timeout:
|
||||
vms = VirtualMachine.list(apiclient, id=vmid)
|
||||
vm_list_validation_result = validateList(vms)
|
||||
if vm_list_validation_result[0] == PASS and vm_list_validation_result[1].state == RUNNING:
|
||||
return timeout
|
||||
time.sleep(sleep)
|
||||
timeout = timeout - 1
|
||||
|
||||
return timeout
|
||||
|
||||
def checkCPUAndMemory(self, ssh, service_offering):
|
||||
cpuinfo = ssh.execute("cat /proc/cpuinfo")
|
||||
cpu_cnt = len([i for i in cpuinfo if "processor" in i])
|
||||
# 'cpu MHz\t\t: 2660.499'
|
||||
cpu_speed = [i for i in cpuinfo if "cpu MHz" in i][0].split()[3]
|
||||
meminfo = ssh.execute("cat /proc/meminfo")
|
||||
# MemTotal: 1017464 kB
|
||||
total_mem = [i for i in meminfo if "MemTotal" in i][0].split()[1]
|
||||
|
||||
self.debug(
|
||||
"CPU count: %s, CPU Speed: %s, Mem Info: %s" % (cpu_cnt, cpu_speed, total_mem)
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
int(cpu_cnt),
|
||||
service_offering.cpunumber,
|
||||
"Check CPU Count for service offering"
|
||||
)
|
||||
|
||||
range = 40
|
||||
if self.hypervisor.lower() == "hyperv":
|
||||
range = 200
|
||||
self.assertTrue(
|
||||
isAlmostEqual(int(int(total_mem) / 1024),
|
||||
int(service_offering.memory),
|
||||
range=range
|
||||
),
|
||||
"Check Memory(kb) for service offering"
|
||||
)
|
||||
|
||||
@attr(tags=["advanced", "smoke"], required_hardware="true")
|
||||
def test_change_service_offering_for_vm_with_snapshots(self):
|
||||
"""Test to change service offering for instances with vm snapshots
|
||||
"""
|
||||
|
||||
# 1) Create Virtual Machine using service offering 1
|
||||
self.debug("Creating VM using Service Offering 1")
|
||||
virtual_machine = VirtualMachine.create(
|
||||
self.apiclient,
|
||||
self.services["small"],
|
||||
accountid=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
templateid=self.template.id,
|
||||
zoneid=self.zone.id,
|
||||
hypervisor=self.hypervisor,
|
||||
mode=self.zone.networktype,
|
||||
serviceofferingid=self.service_offering_1.id
|
||||
)
|
||||
|
||||
# Verify Service OFfering 1 CPU cores and memory
|
||||
try:
|
||||
ssh_client = virtual_machine.get_ssh_client(reconnect=True)
|
||||
self.checkCPUAndMemory(ssh_client, self.service_offering_1)
|
||||
except Exception as e:
|
||||
self.fail("SSH failed for virtual machine: %s - %s" % (virtual_machine.ipaddress, e))
|
||||
|
||||
# 2) Take VM Snapshot
|
||||
self.debug("Taking VM Snapshot for VM - ID: %s" % virtual_machine.id)
|
||||
vm_snapshot = VmSnapshot.create(
|
||||
self.apiclient,
|
||||
virtual_machine.id,
|
||||
)
|
||||
|
||||
# 3) Stop Virtual Machine
|
||||
self.debug("Stopping VM - ID: %s" % virtual_machine.id)
|
||||
try:
|
||||
virtual_machine.stop(self.apiclient)
|
||||
except Exception as e:
|
||||
self.fail("Failed to stop VM: %s" % e)
|
||||
|
||||
# 4) Change service offering for VM with snapshots from Service Offering 1 to Service Offering 2
|
||||
self.debug("Changing service offering from Service Offering 1 to Service Offering 2 for VM - ID: %s" % virtual_machine.id)
|
||||
virtual_machine.change_service_offering(self.apiclient, self.service_offering_2.id)
|
||||
|
||||
# 5) Start VM
|
||||
self.debug("Starting VM - ID: %s" % virtual_machine.id)
|
||||
try:
|
||||
virtual_machine.start(self.apiclient)
|
||||
except Exception as e:
|
||||
self.fail("Failed to start virtual machine: %s, %s" % (virtual_machine.name, e))
|
||||
|
||||
# Wait for vm to start
|
||||
timeout = self.wait_vm_start(self.apiclient, virtual_machine.id, self.services["timeout"],
|
||||
self.services["sleep"])
|
||||
if timeout == 0:
|
||||
self.fail("The virtual machine %s failed to start even after %s minutes"
|
||||
% (virtual_machine.name, self.services["timeout"]))
|
||||
|
||||
list_vm_response = list_virtual_machines(
|
||||
self.apiclient,
|
||||
id=virtual_machine.id
|
||||
)
|
||||
self.assertEqual(
|
||||
isinstance(list_vm_response, list),
|
||||
True,
|
||||
"Check list response returns a valid list"
|
||||
)
|
||||
self.assertNotEqual(
|
||||
len(list_vm_response),
|
||||
0,
|
||||
"Check VM avaliable in List Virtual Machines"
|
||||
)
|
||||
self.assertEqual(
|
||||
list_vm_response[0].state,
|
||||
"Running",
|
||||
"Check virtual machine is in running state"
|
||||
)
|
||||
self.assertEqual(
|
||||
list_vm_response[0].id,
|
||||
virtual_machine.id,
|
||||
"Check virtual machine id"
|
||||
)
|
||||
|
||||
# 6) Verify service offering has changed
|
||||
try:
|
||||
ssh_client_2 = virtual_machine.get_ssh_client(reconnect=True)
|
||||
self.checkCPUAndMemory(ssh_client_2, self.service_offering_2)
|
||||
except Exception as e:
|
||||
self.fail("SSH failed for virtual machine: %s - %s" % (virtual_machine.ipaddress, e))
|
||||
|
||||
# 7) Stop Virtual Machine
|
||||
self.debug("Stopping VM - ID: %s" % virtual_machine.id)
|
||||
try:
|
||||
virtual_machine.stop(self.apiclient)
|
||||
except Exception as e:
|
||||
self.fail("Failed to stop VM: %s" % e)
|
||||
|
||||
# 8) Revert to VM Snapshot
|
||||
self.debug("Revert to vm snapshot: %s" % vm_snapshot.id)
|
||||
try:
|
||||
VmSnapshot.revertToSnapshot(
|
||||
self.apiclient,
|
||||
vm_snapshot.id
|
||||
)
|
||||
except Exception as e:
|
||||
self.fail("Failed to revert to VM Snapshot: %s - %s" % (vm_snapshot.id, e))
|
||||
|
||||
# 9) Start VM
|
||||
self.debug("Starting VM - ID: %s" % virtual_machine.id)
|
||||
try:
|
||||
virtual_machine.start(self.apiclient)
|
||||
except Exception as e:
|
||||
self.fail("Failed to start virtual machine: %s, %s" % (virtual_machine.name, e))
|
||||
|
||||
# 10) Verify service offering has changed to Service Offering 1 (from VM Snapshot)
|
||||
try:
|
||||
ssh_client_3 = virtual_machine.get_ssh_client(reconnect=True)
|
||||
self.checkCPUAndMemory(ssh_client_3, self.service_offering_1)
|
||||
except Exception as e:
|
||||
self.fail("SSH failed for virtual machine: %s - %s" % (virtual_machine.ipaddress, e))
|
||||
|
||||
return
|
||||
Loading…
Reference in New Issue