server: fix volume migration on user vm scale (#6704)

Fixes #6701

When volume migration is initiated by system, account check is not needed.

Introduces a new global setting - allow.diskoffering.change.during.scale.vm. This determines whether to allow or disallow disk offering change for root volume during scaling of a stopped or running VM.

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
Co-authored-by: Harikrishna Patnala <harikrishna.patnala@gmail.com>
Co-authored-by: Rohit Yadav <rohityadav89@gmail.com>
Co-authored-by: Daniel Augusto Veronezi Salvador <38945620+GutoVeronezi@users.noreply.github.com>
Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2022-11-30 12:58:48 +05:30 committed by GitHub
parent dbc2032077
commit 47946db888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 517 additions and 67 deletions

View File

@ -405,7 +405,7 @@ public interface ManagementService {
*/
Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForMigrationOfVolume(Long volumeId);
Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForMigrationOfVolumeInternal(Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean keepSourceStoragePool, boolean bypassStorageTypeCheck);
Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForSystemMigrationOfVolume(Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean keepSourceStoragePool, boolean bypassStorageTypeCheck);
String[] listEventTypes();

View File

@ -1526,7 +1526,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Override
public Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForMigrationOfVolume(final Long volumeId) {
Pair<List<? extends StoragePool>, List<? extends StoragePool>> allPoolsAndSuitablePoolsPair = listStoragePoolsForMigrationOfVolumeInternal(volumeId, null, null, null, null, false, true);
Pair<List<? extends StoragePool>, List<? extends StoragePool>> allPoolsAndSuitablePoolsPair = listStoragePoolsForMigrationOfVolumeInternal(volumeId, null, null, null, null, false, true, false);
List<? extends StoragePool> allPools = allPoolsAndSuitablePoolsPair.first();
List<? extends StoragePool> suitablePools = allPoolsAndSuitablePoolsPair.second();
List<StoragePool> avoidPools = new ArrayList<>();
@ -1542,13 +1542,20 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
return new Pair<List<? extends StoragePool>, List<? extends StoragePool>>(allPools, suitablePools);
}
public Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForMigrationOfVolumeInternal(final Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean keepSourceStoragePool, boolean bypassStorageTypeCheck) {
final Account caller = getCaller();
if (!_accountMgr.isRootAdmin(caller.getId())) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Caller is not a root admin, permission denied to migrate the volume");
@Override
public Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForSystemMigrationOfVolume(final Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean keepSourceStoragePool, boolean bypassStorageTypeCheck) {
return listStoragePoolsForMigrationOfVolumeInternal(volumeId, newDiskOfferingId, newSize, newMinIops, newMaxIops, keepSourceStoragePool, bypassStorageTypeCheck, true);
}
public Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForMigrationOfVolumeInternal(final Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean keepSourceStoragePool, boolean bypassStorageTypeCheck, boolean bypassAccountCheck) {
if (!bypassAccountCheck) {
final Account caller = getCaller();
if (!_accountMgr.isRootAdmin(caller.getId())) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Caller is not a root admin, permission denied to migrate the volume");
}
throw new PermissionDeniedException("No permission to migrate volume, only root admin can migrate a volume");
}
throw new PermissionDeniedException("No permission to migrate volume, only root admin can migrate a volume");
}
final VolumeVO volume = _volumeDao.findById(volumeId);

View File

@ -96,6 +96,7 @@ import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToSt
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -1767,14 +1768,14 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
return volume;
}
if (currentSize != newSize || newMaxIops != volume.getMaxIops() || newMinIops != volume.getMinIops()) {
if (currentSize != newSize || !compareEqualsIncludingNullOrZero(newMaxIops, volume.getMaxIops()) || !compareEqualsIncludingNullOrZero(newMinIops, volume.getMinIops())) {
volumeResizeRequired = true;
validateVolumeReadyStateAndHypervisorChecks(volume, currentSize, newSize);
}
StoragePoolVO existingStoragePool = _storagePoolDao.findById(volume.getPoolId());
Pair<List<? extends StoragePool>, List<? extends StoragePool>> poolsPair = managementService.listStoragePoolsForMigrationOfVolumeInternal(volume.getId(), newDiskOffering.getId(), newSize, newMinIops, newMaxIops, true, false);
Pair<List<? extends StoragePool>, List<? extends StoragePool>> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOffering.getId(), newSize, newMinIops, newMaxIops, true, false);
List<? extends StoragePool> suitableStoragePools = poolsPair.second();
if (!suitableStoragePools.stream().anyMatch(p -> (p.getId() == existingStoragePool.getId()))) {
@ -1823,6 +1824,21 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
return volume;
}
/**
* This method is to compare long values, in miniops and maxiops a or b can be null or 0.
* Use this method to treat 0 and null as same
*
* @param a
* @param b
* @return true if a and b are equal excluding 0 and null values.
*/
private boolean compareEqualsIncludingNullOrZero(Long a, Long b) {
a = ObjectUtils.defaultIfNull(a, 0L);
b = ObjectUtils.defaultIfNull(b, 0L);
return a.equals(b);
}
/**
* Returns true if the new disk offering is the same than current offering, and the respective Service offering is a custom (constraint or unconstraint) offering.
*/

View File

@ -47,9 +47,12 @@ import com.cloud.utils.Pair;
*/
public interface UserVmManager extends UserVmService {
String EnableDynamicallyScaleVmCK = "enable.dynamic.scale.vm";
String AllowDiskOfferingChangeDuringScaleVmCK = "allow.diskoffering.change.during.scale.vm";
String AllowUserExpungeRecoverVmCK ="allow.user.expunge.recover.vm";
ConfigKey<Boolean> EnableDynamicallyScaleVm = new ConfigKey<Boolean>("Advanced", Boolean.class, EnableDynamicallyScaleVmCK, "false",
"Enables/Disables dynamically scaling a vm", true, ConfigKey.Scope.Zone);
ConfigKey<Boolean> AllowDiskOfferingChangeDuringScaleVm = new ConfigKey<Boolean>("Advanced", Boolean.class, AllowDiskOfferingChangeDuringScaleVmCK, "false",
"Determines whether to allow or disallow disk offering change for root volume during scaling of a stopped or running vm", true, ConfigKey.Scope.Zone);
ConfigKey<Boolean> AllowUserExpungeRecoverVm = new ConfigKey<Boolean>("Advanced", Boolean.class, AllowUserExpungeRecoverVmCK, "false",
"Determines whether users can expunge or recover their vm", true, ConfigKey.Scope.Account);
ConfigKey<Boolean> DisplayVMOVFProperties = new ConfigKey<Boolean>("Advanced", Boolean.class, "vm.display.ovf.properties", "false",

View File

@ -1241,7 +1241,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// resize and migrate the root volume if required
DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newServiceOffering.getDiskOfferingId());
changeDiskOfferingForRootVolume(vmId, newDiskOffering, customParameters);
changeDiskOfferingForRootVolume(vmId, newDiskOffering, customParameters, vmInstance.getDataCenterId());
_itMgr.upgradeVmDb(vmId, newServiceOffering, currentServiceOffering);
@ -2000,7 +2000,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// #3 resize or migrate the root volume if required
DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newServiceOffering.getDiskOfferingId());
changeDiskOfferingForRootVolume(vmId, newDiskOffering, customParameters);
changeDiskOfferingForRootVolume(vmId, newDiskOffering, customParameters, vmInstance.getDataCenterId());
// #4 scale the vm now
vmInstance = _vmInstanceDao.findById(vmId);
@ -2036,7 +2036,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
}
private void changeDiskOfferingForRootVolume(Long vmId, DiskOfferingVO newDiskOffering, Map<String, String> customParameters) throws ResourceAllocationException {
private void changeDiskOfferingForRootVolume(Long vmId, DiskOfferingVO newDiskOffering, Map<String, String> customParameters, Long zoneId) throws ResourceAllocationException {
if (!AllowDiskOfferingChangeDuringScaleVm.valueIn(zoneId)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug(String.format("Changing the disk offering of the root volume during the compute offering change operation is disabled. Please check the setting [%s].", AllowDiskOfferingChangeDuringScaleVm.key()));
}
return;
}
List<VolumeVO> vols = _volsDao.findReadyAndAllocatedRootVolumesByInstance(vmId);
@ -7791,7 +7798,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {EnableDynamicallyScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax,
return new ConfigKey<?>[] {EnableDynamicallyScaleVm, AllowDiskOfferingChangeDuringScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax,
VmIpFetchThreadPoolMax, VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig, DisplayVMOVFProperties,
KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList};
}

View File

@ -794,7 +794,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
continue;
}
LOGGER.debug(String.format("Volume %s needs to be migrated", volumeVO.getUuid()));
Pair<List<? extends StoragePool>, List<? extends StoragePool>> poolsPair = managementService.listStoragePoolsForMigrationOfVolumeInternal(profile.getVolumeId(), null, null, null, null, false, true);
Pair<List<? extends StoragePool>, List<? extends StoragePool>> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(profile.getVolumeId(), null, null, null, null, false, true);
if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) {
cleanupFailedImportVM(vm);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed for unmanaged vm: %s during volume ID: %s migration as no suitable pool(s) found", userVm.getInstanceName(), volumeVO.getUuid()));

View File

@ -25,10 +25,13 @@ from marvin.lib.base import (Account,
Host,
VirtualMachine,
ServiceOffering,
DiskOffering,
Template,
Configurations)
Configurations,
Volume)
from marvin.lib.common import (get_zone,
get_template,
get_test_template,
get_domain)
from nose.plugins.attrib import attr
from marvin.sshClient import SshClient
@ -54,7 +57,7 @@ class TestScaleVm(cloudstackTestCase):
return
# Get Zone, Domain and templates
domain = get_domain(cls.apiclient)
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.services['mode'] = cls.zone.networktype
@ -73,14 +76,19 @@ class TestScaleVm(cloudstackTestCase):
isdynamicallyscalable='true'
)
else:
cls.template = Template.register(
cls.apiclient,
cls.services["CentOS7template"],
zoneid=cls.zone.id
)
cls._cleanup.append(cls.template)
cls.template.download(cls.apiclient)
time.sleep(60)
cls.template = get_test_template(
cls.apiclient,
cls.zone.id,
cls.hypervisor
)
if cls.template == FAILED:
assert False, "get_test_template() failed to return template\
with hypervisor %s" % cls.hypervisor
cls.template = Template.update(
cls.template,
cls.apiclient,
isdynamicallyscalable='true'
)
# Set Zones and disk offerings
cls.services["small"]["zoneid"] = cls.zone.id
@ -90,7 +98,7 @@ class TestScaleVm(cloudstackTestCase):
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
domainid=domain.id
domainid=cls.domain.id
)
cls._cleanup.append(cls.account)
@ -124,37 +132,6 @@ class TestScaleVm(cloudstackTestCase):
dynamicscalingenabled=False
)
# create a virtual machine
cls.virtual_machine = VirtualMachine.create(
cls.apiclient,
cls.services["small"],
accountid=cls.account.name,
domainid=cls.account.domainid,
serviceofferingid=cls.small_offering.id,
mode=cls.services["mode"]
)
# create a virtual machine which cannot be dynamically scalable
cls.virtual_machine_with_service_offering_dynamic_scaling_disabled = VirtualMachine.create(
cls.apiclient,
cls.services["small"],
accountid=cls.account.name,
domainid=cls.account.domainid,
serviceofferingid=cls.small_offering_dynamic_scaling_disabled.id,
mode=cls.services["mode"]
)
# create a virtual machine which cannot be dynamically scalable
cls.virtual_machine_not_dynamically_scalable = VirtualMachine.create(
cls.apiclient,
cls.services["small"],
accountid=cls.account.name,
domainid=cls.account.domainid,
serviceofferingid=cls.small_offering.id,
mode=cls.services["mode"],
dynamicscalingenabled=False
)
cls._cleanup = [
cls.small_offering,
cls.big_offering,
@ -170,6 +147,11 @@ class TestScaleVm(cloudstackTestCase):
name="enable.dynamic.scale.vm",
value="false"
)
Configurations.update(
cls.apiclient,
name="allow.diskOffering.change.during.scale.vm",
value="false"
)
super(TestScaleVm,cls).tearDownClass()
return
@ -177,6 +159,7 @@ class TestScaleVm(cloudstackTestCase):
self.apiclient = self.testClient.getApiClient()
self.dbclient = self.testClient.getDbConnection()
self.cleanup = []
self.services["disk_offering"]["disksize"] = 2
if self.unsupportedHypervisor:
self.skipTest("Skipping test because unsupported hypervisor\
@ -199,6 +182,9 @@ class TestScaleVm(cloudstackTestCase):
return ssh_client
def is_host_xcpng8(self, hostname):
return type(hostname) == list and len(hostname) > 0 and 'XCP-ng 8' in hostname[0]
@attr(hypervisor="xenserver")
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_01_scale_vm(self):
@ -216,6 +202,17 @@ class TestScaleVm(cloudstackTestCase):
# scaling is not
# guaranteed until tools are installed, vm rebooted
# create a virtual machine
self.virtual_machine = VirtualMachine.create(
self.apiclient,
self.services["small"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.small_offering.id,
mode=self.services["mode"]
)
self.cleanup.append(self.virtual_machine)
# If hypervisor is Vmware, then check if
# the vmware tools are installed and the process is running
# Vmware tools are necessary for scale VM operation
@ -227,6 +224,7 @@ class TestScaleVm(cloudstackTestCase):
if not "running" in result:
self.skipTest("Skipping scale VM operation because\
VMware tools are not installed on the VM")
res = None
if self.hypervisor.lower() != 'simulator':
hostid = self.virtual_machine.hostid
host = Host.list(
@ -251,14 +249,27 @@ class TestScaleVm(cloudstackTestCase):
self.apiclient,
isdynamicallyscalable='true')
if self.is_host_xcpng8(res):
self.debug("Only scaling for CPU for XCP-ng 8")
offering_data = self.services["service_offerings"]["big"]
offering_data["cpunumber"] = 2
offering_data["memory"] = self.virtual_machine.memory
self.bigger_offering = ServiceOffering.create(
self.apiclient,
offering_data
)
self.cleanup.append(self.bigger_offering)
else:
self.bigger_offering = self.big_offering
self.debug("Scaling VM-ID: %s to service offering: %s and state %s" % (
self.virtual_machine.id,
self.big_offering.id,
self.bigger_offering.id,
self.virtual_machine.state
))
cmd = scaleVirtualMachine.scaleVirtualMachineCmd()
cmd.serviceofferingid = self.big_offering.id
cmd.serviceofferingid = self.bigger_offering.id
cmd.id = self.virtual_machine.id
try:
@ -297,11 +308,11 @@ class TestScaleVm(cloudstackTestCase):
offering %s and the response says %s" %
(self.virtual_machine.id,
self.virtual_machine.serviceofferingid,
self.big_offering.id,
self.bigger_offering.id,
vm_response.serviceofferingid))
self.assertEqual(
vm_response.serviceofferingid,
self.big_offering.id,
self.bigger_offering.id,
"Check service offering of the VM"
)
@ -313,7 +324,7 @@ class TestScaleVm(cloudstackTestCase):
return
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_02_scale_vm(self):
def test_02_scale_vm_negative_offering_disable_scaling(self):
"""Test scale virtual machine which is created from a service offering for which dynamicscalingenabled is false. Scaling operation should fail.
"""
@ -325,6 +336,17 @@ class TestScaleVm(cloudstackTestCase):
# scaling is not
# guaranteed until tools are installed, vm rebooted
# create a virtual machine which cannot be dynamically scalable
self.virtual_machine_with_service_offering_dynamic_scaling_disabled = VirtualMachine.create(
self.apiclient,
self.services["small"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.small_offering_dynamic_scaling_disabled.id,
mode=self.services["mode"]
)
self.cleanup.append(self.virtual_machine_with_service_offering_dynamic_scaling_disabled)
# If hypervisor is Vmware, then check if
# the vmware tools are installed and the process is running
# Vmware tools are necessary for scale VM operation
@ -368,7 +390,7 @@ class TestScaleVm(cloudstackTestCase):
self.debug("Scaling VM-ID: %s to service offering: %s for which dynamic scaling is disabled and VM state %s" % (
self.virtual_machine_with_service_offering_dynamic_scaling_disabled.id,
self.big_offering_dynamic_scaling_disabled.id,
self.virtual_machine.state
self.virtual_machine_with_service_offering_dynamic_scaling_disabled.state
))
cmd = scaleVirtualMachine.scaleVirtualMachineCmd()
@ -388,7 +410,7 @@ class TestScaleVm(cloudstackTestCase):
self.debug("Scaling VM-ID: %s to service offering: %s for which dynamic scaling is enabled and VM state %s" % (
self.virtual_machine_with_service_offering_dynamic_scaling_disabled.id,
self.big_offering.id,
self.virtual_machine.state
self.virtual_machine_with_service_offering_dynamic_scaling_disabled.state
))
cmd = scaleVirtualMachine.scaleVirtualMachineCmd()
@ -408,7 +430,7 @@ class TestScaleVm(cloudstackTestCase):
return
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_03_scale_vm(self):
def test_03_scale_vm_negative_vm_disable_scaling(self):
"""Test scale virtual machine which is not dynamically scalable to a service offering. Scaling operation should fail.
"""
# Validate the following
@ -422,6 +444,18 @@ class TestScaleVm(cloudstackTestCase):
# scaling is not
# guaranteed until tools are installed, vm rebooted
# create a virtual machine which cannot be dynamically scalable
self.virtual_machine_not_dynamically_scalable = VirtualMachine.create(
self.apiclient,
self.services["small"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.small_offering.id,
mode=self.services["mode"],
dynamicscalingenabled=False
)
self.cleanup.append(self.virtual_machine_not_dynamically_scalable)
# If hypervisor is Vmware, then check if
# the vmware tools are installed and the process is running
# Vmware tools are necessary for scale VM operation
@ -463,7 +497,7 @@ class TestScaleVm(cloudstackTestCase):
self.debug("Scaling VM-ID: %s to service offering: %s for which dynamic scaling is enabled and VM state %s" % (
self.virtual_machine_not_dynamically_scalable.id,
self.big_offering.id,
self.virtual_machine.state
self.virtual_machine_not_dynamically_scalable.state
))
cmd = scaleVirtualMachine.scaleVirtualMachineCmd()
@ -480,4 +514,387 @@ class TestScaleVm(cloudstackTestCase):
else:
self.fail("Expected an exception to be thrown, failing")
return
return
@attr(hypervisor="xenserver")
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_04_scale_vm_with_user_account(self):
"""Test scale virtual machine under useraccount
"""
# Validate the following
# Create a user Account and create a VM in it.
# Scale up the vm and see if it scales to the new svc offering
# Create an user account
self.userAccount = Account.create(
self.apiclient,
self.services["account"],
admin=False,
domainid=self.domain.id
)
self.cleanup.append(self.userAccount)
# Create user api client of the user account
self.userapiclient = self.testClient.getUserApiClient(
UserName=self.userAccount.name,
DomainName=self.userAccount.domain
)
# create a virtual machine
self.virtual_machine_in_user_account = VirtualMachine.create(
self.userapiclient,
self.services["small"],
accountid=self.userAccount.name,
domainid=self.userAccount.domainid,
serviceofferingid=self.small_offering.id,
mode=self.services["mode"]
)
if self.hypervisor.lower() == "vmware":
sshClient = self.virtual_machine_in_user_account.get_ssh_client()
result = str(
sshClient.execute("service vmware-tools status")).lower()
self.debug("and result is: %s" % result)
if not "running" in result:
self.skipTest("Skipping scale VM operation because\
VMware tools are not installed on the VM")
res = None
if self.hypervisor.lower() != 'simulator':
list_vm_response = VirtualMachine.list(
self.apiclient,
id=self.virtual_machine_in_user_account.id
)[0]
hostid = list_vm_response.hostid
host = Host.list(
self.apiclient,
zoneid=self.zone.id,
hostid=hostid,
type='Routing'
)[0]
try:
username = self.hostConfig["username"]
password = self.hostConfig["password"]
ssh_client = self.get_ssh_client(host.ipaddress, username, password)
res = ssh_client.execute("hostnamectl | grep 'Operating System' | cut -d':' -f2")
except Exception as e:
pass
if 'XenServer' in res[0]:
self.skipTest("Skipping test for XenServer as it's License does not allow scaling")
self.virtual_machine_in_user_account.update(
self.userapiclient,
isdynamicallyscalable='true')
if self.is_host_xcpng8(res):
self.debug("Only scaling for CPU for XCP-ng 8")
offering_data = self.services["service_offerings"]["big"]
offering_data["cpunumber"] = 2
offering_data["memory"] = self.virtual_machine_in_user_account.memory
self.bigger_offering = ServiceOffering.create(
self.apiclient,
offering_data
)
self.cleanup.append(self.bigger_offering)
else:
self.bigger_offering = self.big_offering
self.debug("Scaling VM-ID: %s to service offering: %s and state %s" % (
self.virtual_machine_in_user_account.id,
self.bigger_offering.id,
self.virtual_machine_in_user_account.state
))
cmd = scaleVirtualMachine.scaleVirtualMachineCmd()
cmd.serviceofferingid = self.bigger_offering.id
cmd.id = self.virtual_machine_in_user_account.id
try:
self.userapiclient.scaleVirtualMachine(cmd)
except Exception as e:
if "LicenceRestriction" in str(e):
self.skipTest("Your XenServer License does not allow scaling")
else:
self.fail("Scaling failed with the following exception: " + str(e))
list_vm_response = VirtualMachine.list(
self.userapiclient,
id=self.virtual_machine_in_user_account.id
)
vm_response = list_vm_response[0]
self.debug(
"Scaling VM-ID: %s from service offering: %s to new service\
offering %s and the response says %s" %
(self.virtual_machine_in_user_account.id,
self.virtual_machine_in_user_account.serviceofferingid,
self.bigger_offering.id,
vm_response.serviceofferingid))
self.assertEqual(
vm_response.serviceofferingid,
self.bigger_offering.id,
"Check service offering of the VM"
)
self.assertEqual(
vm_response.state,
'Running',
"Check the state of VM"
)
return
@attr(hypervisor="xenserver")
@attr(tags=["advanced", "basic"], required_hardware="false")
def test_05_scale_vm_dont_allow_disk_offering_change(self):
"""Test scale virtual machine with disk offering changes
"""
# Validate the following two cases
# 1
# create serviceoffering1 with diskoffering1
# create serviceoffering2 with diskoffering2
# create a VM with serviceoffering1
# Scale up the vm to serviceoffering2
# Check disk offering of root volume to be diskoffering1 since setting allow.diskOffering.change.during.scale.vm is false
# 2
# create serviceoffering3 with diskoffering3
# update setting allow.diskOffering.change.during.scale.vm to true
# scale up the VM to serviceoffering3
# Check disk offering of root volume to be diskoffering3 since setting allow.diskOffering.change.during.scale.vm is true
self.disk_offering1 = DiskOffering.create(
self.apiclient,
self.services["disk_offering"],
)
self._cleanup.append(self.disk_offering1)
offering_data = {
'displaytext': 'ServiceOffering1WithDiskOffering1',
'cpuspeed': 500,
'cpunumber': 1,
'name': 'ServiceOffering1WithDiskOffering1',
'memory': 512,
'diskofferingid': self.disk_offering1.id
}
self.ServiceOffering1WithDiskOffering1 = ServiceOffering.create(
self.apiclient,
offering_data,
)
self._cleanup.append(self.ServiceOffering1WithDiskOffering1)
# create a virtual machine
self.virtual_machine_test = VirtualMachine.create(
self.apiclient,
self.services["small"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.ServiceOffering1WithDiskOffering1.id,
mode=self.services["mode"]
)
if self.hypervisor.lower() == "vmware":
sshClient = self.virtual_machine_test.get_ssh_client()
result = str(
sshClient.execute("service vmware-tools status")).lower()
self.debug("and result is: %s" % result)
if not "running" in result:
self.skipTest("Skipping scale VM operation because\
VMware tools are not installed on the VM")
res = None
if self.hypervisor.lower() != 'simulator':
hostid = self.virtual_machine_test.hostid
host = Host.list(
self.apiclient,
zoneid=self.zone.id,
hostid=hostid,
type='Routing'
)[0]
try:
username = self.hostConfig["username"]
password = self.hostConfig["password"]
ssh_client = self.get_ssh_client(host.ipaddress, username, password)
res = ssh_client.execute("hostnamectl | grep 'Operating System' | cut -d':' -f2")
except Exception as e:
pass
if 'XenServer' in res[0]:
self.skipTest("Skipping test for XenServer as it's License does not allow scaling")
self.virtual_machine_test.update(
self.apiclient,
isdynamicallyscalable='true')
self.disk_offering2 = DiskOffering.create(
self.apiclient,
self.services["disk_offering"],
)
self._cleanup.append(self.disk_offering2)
offering_data = {
'displaytext': 'ServiceOffering2WithDiskOffering2',
'cpuspeed': 1000,
'cpunumber': 2,
'name': 'ServiceOffering2WithDiskOffering2',
'memory': 1024,
'diskofferingid': self.disk_offering2.id
}
if self.is_host_xcpng8(res):
self.debug("Only scaling for CPU for XCP-ng 8")
offering_data["memory"] = self.virtual_machine_test.memory
self.ServiceOffering2WithDiskOffering2 = ServiceOffering.create(
self.apiclient,
offering_data,
)
self._cleanup.append(self.ServiceOffering2WithDiskOffering2)
self.debug("Scaling VM-ID: %s to service offering: %s and state %s" % (
self.virtual_machine_test.id,
self.ServiceOffering2WithDiskOffering2.id,
self.virtual_machine_test.state
))
cmd = scaleVirtualMachine.scaleVirtualMachineCmd()
cmd.serviceofferingid = self.ServiceOffering2WithDiskOffering2.id
cmd.id = self.virtual_machine_test.id
try:
self.apiclient.scaleVirtualMachine(cmd)
except Exception as e:
if "LicenceRestriction" in str(e):
self.skipTest("Your XenServer License does not allow scaling")
else:
self.fail("Scaling failed with the following exception: " + str(e))
list_vm_response = VirtualMachine.list(
self.apiclient,
id=self.virtual_machine_test.id
)
vm_response = list_vm_response[0]
self.debug(
"Scaling VM-ID: %s from service offering: %s to new service\
offering %s and the response says %s" %
(self.virtual_machine_test.id,
self.virtual_machine_test.serviceofferingid,
self.ServiceOffering2WithDiskOffering2.id,
vm_response.serviceofferingid))
vm_response = list_vm_response[0]
self.assertEqual(
vm_response.serviceofferingid,
self.ServiceOffering2WithDiskOffering2.id,
"Check service offering of the VM"
)
volume_response = Volume.list(
self.apiclient,
virtualmachineid=self.virtual_machine_test.id,
listall=True
)[0]
self.assertEqual(
volume_response.diskofferingid,
self.disk_offering1.id,
"Check disk offering of the VM, this should not change since allow.diskOffering.change.during.scale.vm is false"
)
# Do same scale vm operation with allow.diskOffering.change.during.scale.vm value to true
if self.hypervisor.lower() in ['simulator']:
self.debug("Simulator doesn't support changing disk offering, volume resize")
return
disk_offering_data = self.services["disk_offering"]
if self.hypervisor.lower() in ['xenserver']:
self.debug("For hypervisor %s, do not resize volume and just change try to change the disk offering")
volume_response = Volume.list(
self.apiclient,
virtualmachineid=self.virtual_machine_test.id,
listall=True
)[0]
disk_offering_data["disksize"] = int(volume_response.size / (1024 ** 3))
self.disk_offering3 = DiskOffering.create(
self.apiclient,
disk_offering_data,
)
self._cleanup.append(self.disk_offering3)
offering_data = {
'displaytext': 'ServiceOffering3WithDiskOffering3',
'cpuspeed': 1500,
'cpunumber': 2,
'name': 'ServiceOffering3WithDiskOffering3',
'memory': 1024,
'diskofferingid': self.disk_offering3.id
}
if self.is_host_xcpng8(res):
self.debug("Only scaling for CPU for XCP-ng 8")
offering_data["memory"] = vm_response.memory
self.ServiceOffering3WithDiskOffering3 = ServiceOffering.create(
self.apiclient,
offering_data,
)
self._cleanup.append(self.ServiceOffering3WithDiskOffering3)
Configurations.update(
self.apiclient,
name="allow.diskOffering.change.during.scale.vm",
value="true"
)
self.debug("Scaling VM-ID: %s to service offering: %s and state %s" % (
self.virtual_machine_test.id,
self.ServiceOffering3WithDiskOffering3.id,
self.virtual_machine_test.state
))
cmd = scaleVirtualMachine.scaleVirtualMachineCmd()
cmd.serviceofferingid = self.ServiceOffering3WithDiskOffering3.id
cmd.id = self.virtual_machine_test.id
try:
self.apiclient.scaleVirtualMachine(cmd)
except Exception as e:
if "LicenceRestriction" in str(e):
self.skipTest("Your XenServer License does not allow scaling")
else:
self.fail("Scaling failed with the following exception: " + str(e))
list_vm_response = VirtualMachine.list(
self.apiclient,
id=self.virtual_machine_test.id
)
vm_response = list_vm_response[0]
self.debug(
"Scaling VM-ID: %s from service offering: %s to new service\
offering %s and the response says %s" %
(self.virtual_machine_test.id,
self.virtual_machine_test.serviceofferingid,
self.ServiceOffering3WithDiskOffering3.id,
vm_response.serviceofferingid))
self.assertEqual(
vm_response.serviceofferingid,
self.ServiceOffering3WithDiskOffering3.id,
"Check service offering of the VM"
)
volume_response = Volume.list(
self.apiclient,
virtualmachineid=self.virtual_machine_test.id,
listall=True
)[0]
self.assertEqual(
volume_response.diskofferingid,
self.disk_offering3.id,
"Check disk offering of the VM, this should change since allow.diskOffering.change.during.scale.vm is true"
)
return