CLOUDSTACK-9892: Primary storage resource check is broken when using root disk size override to deploy VM (#2088)

This happens when the root disk size is overridden. The primary storage limit check should be performed based on overridden size instead of template size. Enabled root disk resize tests to run on simulator as well.
This commit is contained in:
koushik-das 2018-01-07 16:18:58 +05:30 committed by Rohit Yadav
parent 25d7d741a7
commit 4d7a9d82cc
3 changed files with 147 additions and 123 deletions

View File

@ -86,9 +86,17 @@ public class SimulatorStorageProcessor implements StorageProcessor {
@Override
public Answer cloneVolumeFromBaseTemplate(CopyCommand cmd) {
long size = 100;
DataTO dataTO = cmd.getDestTO();
if (dataTO instanceof VolumeObjectTO) {
VolumeObjectTO destVolume = (VolumeObjectTO)dataTO;
if (destVolume.getSize() != null) {
size = destVolume.getSize();
}
}
VolumeObjectTO volume = new VolumeObjectTO();
volume.setPath(UUID.randomUUID().toString());
volume.setSize(100);
volume.setSize(size);
volume.setFormat(Storage.ImageFormat.RAW);
return new CopyCmdAnswer(volume);
}

View File

@ -311,9 +311,8 @@ import com.cloud.storage.snapshot.SnapshotApiService;
public class UserVmManagerImpl extends ManagerBase implements UserVmManager, VirtualMachineGuru, UserVmService, Configurable {
private static final Logger s_logger = Logger.getLogger(UserVmManagerImpl.class);
private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3
// seconds
private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3 seconds
private static final long GB_TO_BYTES = 1024 * 1024 * 1024;
@Inject
EntityManager _entityMgr;
@ -3251,6 +3250,19 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
_templateDao.loadDetails(template);
}
HypervisorType hypervisorType = null;
if (template.getHypervisorType() == null || template.getHypervisorType() == HypervisorType.None) {
if (hypervisor == null || hypervisor == HypervisorType.None) {
throw new InvalidParameterValueException("hypervisor parameter is needed to deploy VM or the hypervisor parameter value passed is invalid");
}
hypervisorType = hypervisor;
} else {
if (hypervisor != null && hypervisor != HypervisorType.None && hypervisor != template.getHypervisorType()) {
throw new InvalidParameterValueException("Hypervisor passed to the deployVm call, is different from the hypervisor type of the template");
}
hypervisorType = template.getHypervisorType();
}
long accountId = owner.getId();
assert !(requestedIps != null && (defaultIps.getIp4Address() != null || defaultIps.getIp6Address() != null)) : "requestedIp list and defaultNetworkIp should never be specified together";
@ -3283,11 +3295,25 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
// check if account/domain is with in resource limits to create a new vm
boolean isIso = Storage.ImageFormat.ISO == template.getFormat();
// For baremetal, size can be null
Long tmp = _templateDao.findById(template.getId()).getSize();
long size = 0;
if (tmp != null) {
size = tmp;
// custom root disk size, resizes base template to larger size
if (customParameters.containsKey("rootdisksize")) {
// only KVM, XenServer and VMware supports rootdisksize override
if (!(hypervisorType == HypervisorType.KVM || hypervisorType == HypervisorType.XenServer || hypervisorType == HypervisorType.VMware || hypervisorType == HypervisorType.Simulator)) {
throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support rootdisksize override");
}
Long rootDiskSize = NumbersUtil.parseLong(customParameters.get("rootdisksize"), -1);
if (rootDiskSize <= 0) {
throw new InvalidParameterValueException("Root disk size should be a positive number.");
}
size = rootDiskSize * GB_TO_BYTES;
} else {
// For baremetal, size can be null
Long templateSize = _templateDao.findById(template.getId()).getSize();
if (templateSize != null) {
size = templateSize;
}
}
if (diskOfferingId != null) {
DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId);
@ -3301,7 +3327,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
throw new InvalidParameterValueException("VM Creation failed. Volume size: " + diskSize + "GB is out of allowed range. Max: " + customDiskOfferingMaxSize
+ " Min:" + customDiskOfferingMinSize);
}
size=size+diskSize*(1024*1024*1024);
size += diskSize * GB_TO_BYTES;
}
size += _diskOfferingDao.findById(diskOfferingId).getDiskSize();
}
@ -3359,19 +3385,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
}
HypervisorType hypervisorType = null;
if (template.getHypervisorType() == null || template.getHypervisorType() == HypervisorType.None) {
if (hypervisor == null || hypervisor == HypervisorType.None) {
throw new InvalidParameterValueException("hypervisor parameter is needed to deploy VM or the hypervisor parameter value passed is invalid");
}
hypervisorType = hypervisor;
} else {
if (hypervisor != null && hypervisor != HypervisorType.None && hypervisor != template.getHypervisorType()) {
throw new InvalidParameterValueException("Hypervisor passed to the deployVm call, is different from the hypervisor type of the template");
}
hypervisorType = template.getHypervisorType();
}
if (hypervisorType != HypervisorType.BareMetal) {
// check if we have available pools for vm deployment
long availablePools = _storagePoolDao.countPoolsByStatus(StoragePoolStatus.Up);
@ -3635,8 +3648,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return Transaction.execute(new TransactionCallbackWithException<UserVmVO, InsufficientCapacityException>() {
@Override
public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCapacityException {
UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.getOfferHA(), offering
.getLimitCpuUse(), owner.getDomainId(), owner.getId(), userId, offering.getId(), userData, hostName, diskOfferingId);
UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.getOfferHA(),
offering.getLimitCpuUse(), owner.getDomainId(), owner.getId(), userId, offering.getId(), userData, hostName, diskOfferingId);
vm.setUuid(uuidName);
vm.setDynamicallyScalable(template.isDynamicallyScalable());
@ -3658,16 +3671,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
Long rootDiskSize = null;
// custom root disk size, resizes base template to larger size
if (customParameters.containsKey("rootdisksize")) {
if (NumbersUtil.parseLong(customParameters.get("rootdisksize"), -1) <= 0) {
throw new InvalidParameterValueException("rootdisk size should be a non zero number.");
}
// already verified for positive number
rootDiskSize = Long.parseLong(customParameters.get("rootdisksize"));
// only KVM, XenServer and VMware supports rootdisksize override
if (!(hypervisorType == HypervisorType.KVM || hypervisorType == HypervisorType.XenServer || hypervisorType == HypervisorType.VMware)) {
throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support rootdisksize override");
}
VMTemplateVO templateVO = _templateDao.findById(template.getId());
if (templateVO == null) {
throw new InvalidParameterValueException("Unable to look up template by id " + template.getId());
@ -3767,20 +3773,22 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
{
// rootdisksize must be larger than template.
if ((rootDiskSize << 30) < templateVO.getSize()) {
Long templateVOSizeGB = templateVO.getSize() / 1024 / 1024 / 1024;
s_logger.error("unsupported: rootdisksize override is smaller than template size " + templateVO.getSize() + "B (" + templateVOSizeGB + "GB)");
throw new InvalidParameterValueException("unsupported: rootdisksize override is smaller than template size " + templateVO.getSize() + "B (" + templateVOSizeGB + "GB)");
Long templateVOSizeGB = templateVO.getSize() / GB_TO_BYTES;
String error = "Unsupported: rootdisksize override is smaller than template size " + templateVO.getSize() + "B (" + templateVOSizeGB + "GB)";
s_logger.error(error);
throw new InvalidParameterValueException(error);
} else if ((rootDiskSize << 30) > templateVO.getSize()) {
if (hypervisorType == HypervisorType.VMware && (vm.getDetails() == null || vm.getDetails().get("rootDiskController") == null)) {
if (hypervisorType == HypervisorType.VMware && (vm.getDetails() == null || vm.getDetails().get("rootDiskController") == null)) {
s_logger.warn("If Root disk controller parameter is not overridden, then Root disk resize may fail because current Root disk controller value is NULL.");
} else if (hypervisorType == HypervisorType.VMware && !vm.getDetails().get("rootDiskController").toLowerCase().contains("scsi")) {
s_logger.error("Found unsupported root disk controller : " + vm.getDetails().get("rootDiskController"));
throw new InvalidParameterValueException("Found unsupported root disk controller :" + vm.getDetails().get("rootDiskController"));
} else {
s_logger.debug("Rootdisksize override validation successful. Template root disk size "+(templateVO.getSize() / 1024 / 1024 / 1024)+ " GB" + " Root disk size specified "+ rootDiskSize+" GB");
}
} else if (hypervisorType == HypervisorType.VMware && !vm.getDetails().get("rootDiskController").toLowerCase().contains("scsi")) {
String error = "Found unsupported root disk controller: " + vm.getDetails().get("rootDiskController");
s_logger.error(error);
throw new InvalidParameterValueException(error);
} else {
s_logger.debug("Rootdisksize override validation successful. Template root disk size " + (templateVO.getSize() / GB_TO_BYTES) + "GB Root disk size specified " + rootDiskSize + "GB");
}
} else {
s_logger.debug("Root disk size specified is " + (rootDiskSize << 30) + " and Template root disk size is " + templateVO.getSize()+" . Both are equal so no need to override");
s_logger.debug("Root disk size specified is " + (rootDiskSize << 30) + "B and Template root disk size is " + templateVO.getSize() + "B. Both are equal so no need to override");
customParameters.remove("rootdisksize");
}
}

View File

@ -22,13 +22,14 @@ from marvin.cloudstackTestCase import cloudstackTestCase
#Import Integration Libraries
#base - contains all resources as entities and defines create, delete, list operations on them
from marvin.lib.base import Account, VirtualMachine, ServiceOffering,\
Configurations,StoragePool,Template
Configurations, StoragePool, Template
#utils - utility classes for common cleanup, external library wrappers etc
from marvin.lib.utils import cleanup_resources,validateList
from marvin.lib.utils import cleanup_resources, validateList
from marvin.lib.common import get_zone, get_domain, get_template,\
list_volumes,list_storage_pools,list_configurations
from marvin.codes import FAILED,INVALID_INPUT
from marvin.cloudstackAPI import *
list_volumes, list_storage_pools, list_configurations,\
matchResourceCount
from marvin.codes import FAILED, INVALID_INPUT, PASS,\
RESOURCE_PRIMARY_STORAGE
from nose.plugins.attrib import attr
from marvin.sshClient import SshClient
import time
@ -203,7 +204,7 @@ class TestDeployVmRootSize(cloudstackTestCase):
except Exception:
return False
@attr(tags = ['advanced', 'basic', 'sg'], required_hardware="true")
@attr(tags = ['advanced', 'basic', 'sg'], required_hardware="false")
def test_00_deploy_vm_root_resize(self):
"""Test deploy virtual machine with root resize
@ -214,30 +215,34 @@ class TestDeployVmRootSize(cloudstackTestCase):
"""
newrootsize = (self.template.size >> 30) + 2
if(self.hypervisor.lower() == 'kvm' or self.hypervisor.lower() ==
'xenserver'or self.hypervisor.lower() == 'vmware' ):
if (self.hypervisor.lower() == 'kvm' or self.hypervisor.lower() == 'xenserver'
or self.hypervisor.lower() == 'vmware' or self.hypervisor.lower() == 'simulator'):
accounts = Account.list(self.apiclient, id=self.account.id)
self.assertEqual(validateList(accounts)[0], PASS,
"accounts list validation failed")
initialResourceCount = int(accounts[0].primarystoragetotal)
if self.hypervisor=="vmware":
self.virtual_machine = VirtualMachine.create(
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.domain.id,
serviceofferingid=self.services_offering_vmware.id,
templateid=self.template.id,
rootdisksize=newrootsize
)
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.domain.id,
serviceofferingid=self.services_offering_vmware.id,
templateid=self.tempobj.id,
rootdisksize=newrootsize
)
else:
self.virtual_machine = VirtualMachine.create(
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.domain.id,
serviceofferingid=self.service_offering.id,
templateid=self.template.id,
rootdisksize=newrootsize
)
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.domain.id,
serviceofferingid=self.service_offering.id,
templateid=self.template.id,
rootdisksize=newrootsize
)
list_vms = VirtualMachine.list(self.apiclient, id=self.virtual_machine.id)
self.debug(
@ -246,8 +251,8 @@ class TestDeployVmRootSize(cloudstackTestCase):
)
res=validateList(list_vms)
self.assertNotEqual(res[2],INVALID_INPUT," Invalid list VM "
"response")
self.assertNotEqual(res[2], INVALID_INPUT, "Invalid list VM "
"response")
self.cleanup.append(self.virtual_machine)
vm = list_vms[0]
@ -275,8 +280,8 @@ class TestDeployVmRootSize(cloudstackTestCase):
listall=True
)
res=validateList(list_volume_response)
self.assertNotEqual(res[2],INVALID_INPUT," Invalid list VM "
"response")
self.assertNotEqual(res[2], INVALID_INPUT, "Invalid list VM "
"response")
rootvolume = list_volume_response[0]
success = False
if rootvolume is not None and rootvolume.size == (newrootsize << 30):
@ -288,6 +293,11 @@ class TestDeployVmRootSize(cloudstackTestCase):
"Check if the root volume resized appropriately"
)
response = matchResourceCount(
self.apiclient, (initialResourceCount + newrootsize),
RESOURCE_PRIMARY_STORAGE,
accountid=self.account.id)
self.assertEqual(response[0], PASS, response[1])
else:
self.debug("hypervisor %s unsupported for test 00, verifying it errors properly" % self.hypervisor)
newrootsize = (self.template.size >> 30) + 2
@ -307,22 +317,22 @@ class TestDeployVmRootSize(cloudstackTestCase):
if re.search("Hypervisor \S+ does not support rootdisksize override", str(ex)):
success = True
else:
self.debug("virtual machine create did not fail appropriately. Error was actually : " + str(ex));
self.debug("Virtual machine create did not fail appropriately. Error was actually : " + str(ex));
self.assertEqual(success, True, "Check if unsupported hypervisor %s fails appropriately" % self.hypervisor)
@attr(tags = ['advanced', 'basic', 'sg'], required_hardware="true")
@attr(tags = ['advanced', 'basic', 'sg'], required_hardware="false")
def test_01_deploy_vm_root_resize(self):
"""Test proper failure to deploy virtual machine with rootdisksize of 0
"""
newrootsize=0
success=False
if(self.hypervisor.lower() == 'kvm' or self.hypervisor.lower() ==
'xenserver'or self.hypervisor.lower() == 'vmware' ):
if (self.hypervisor.lower() == 'kvm' or self.hypervisor.lower() == 'xenserver'
or self.hypervisor.lower() == 'vmware' or self.hypervisor.lower() == 'simulator'):
try:
if self.hypervisor=="vmware":
self.virtual_machine = VirtualMachine.create(
self.virtual_machine = VirtualMachine.create(
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
@ -331,9 +341,49 @@ class TestDeployVmRootSize(cloudstackTestCase):
templateid=self.template.id,
rootdisksize=newrootsize
)
else:
self.virtual_machine = VirtualMachine.create(
self.virtual_machine = VirtualMachine.create(
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.domain.id,
serviceofferingid=self.service_offering.id,
templateid=self.template.id,
rootdisksize=newrootsize
)
except Exception as ex:
if "Root disk size should be a positive number" in str(ex):
success = True
else:
self.debug("Virtual machine create did not fail appropriately. Error was actually : " + str(ex));
self.assertEqual(success, True, "Check if passing 0 as rootdisksize fails appropriately")
else:
self.debug("test 01 does not support hypervisor type " + self.hypervisor)
@attr(tags = ['advanced', 'basic', 'sg'], required_hardware="false", BugId="CLOUDSTACK-6984")
def test_02_deploy_vm_root_resize(self):
"""Test proper failure to deploy virtual machine with rootdisksize less than template size
"""
newrootsize = (self.template.size >> 30) - 1
success=False
self.assertEqual(newrootsize > 0, True, "Provided template is less than 1G in size, cannot run test")
if (self.hypervisor.lower() == 'kvm' or self.hypervisor.lower() == 'xenserver'
or self.hypervisor.lower() == 'vmware' or self.hypervisor.lower() == 'simulator'):
try:
if self.hypervisor=="vmware":
self.virtual_machine = VirtualMachine.create(
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.domain.id,
serviceofferingid=self.services_offering_vmware.id,
templateid=self.tempobj.id,
rootdisksize=newrootsize
)
else:
self.virtual_machine = VirtualMachine.create(
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
@ -342,57 +392,15 @@ class TestDeployVmRootSize(cloudstackTestCase):
templateid=self.template.id,
rootdisksize=newrootsize
)
except Exception as ex:
if "rootdisk size should be a non zero number" in str(ex):
success = True
else:
self.debug("virtual machine create did not fail appropriately. Error was actually : " + str(ex));
self.assertEqual(success, True, "Check if passing 0 as rootdisksize fails appropriately")
else:
self.debug("test 01 does not support hypervisor type " + self.hypervisor)
@attr(tags = ['advanced', 'basic', 'sg'], required_hardware="true", BugId="CLOUDSTACK-6984")
def test_02_deploy_vm_root_resize(self):
"""Test proper failure to deploy virtual machine with rootdisksize less than template size
"""
newrootsize = (self.template.size >> 30) - 1
success=False
self.assertEqual(newrootsize > 0, True, "Provided template is less than 1G in size, cannot run test")
if(self.hypervisor.lower() == 'kvm' or self.hypervisor.lower() ==
'xenserver'or self.hypervisor.lower() == 'vmware' ):
try:
if self.hypervisor=="vmware":
self.virtual_machine = VirtualMachine.create(
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.domain.id,
serviceofferingid=self.services_offering_vmware.id,
templateid=self.template.id,
rootdisksize=newrootsize
)
else:
self.virtual_machine = VirtualMachine.create(
self.apiclient, self.services["virtual_machine"],
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.domain.id,
serviceofferingid=self.service_offering.id,
templateid=self.template.id,
rootdisksize=newrootsize
)
except Exception as ex:
if "rootdisksize override is smaller than template size" in str(ex):
success = True
else:
self.debug("virtual machine create did not fail appropriately. Error was actually : " + str(ex));
self.debug("Virtual machine create did not fail appropriately. Error was actually : " + str(ex));
self.assertEqual(success, True, "Check if passing rootdisksize < templatesize fails appropriately")
else:
self.debug("test 02 does not support hypervisor type " +
self.hypervisor)
self.debug("test 02 does not support hypervisor type " + self.hypervisor)
def tearDown(self):
try: