VMware: Early fail spawn if memory is not multiple of 4.

If instance memory is not multiple of 4, creating instance on ESXi
fails with error "['GenericVmConfigFault'] VimFaultException: Memory
(RAM) size is invalid.". However this is after instance is built and
tried to launch on ESXi. Add check in prepare_for_spawn to trigger
failure early and avoid further steps i.e. build as well as launch.

Closes-Bug: #1966987
Change-Id: I7ed8ac986283cd455e54e3f18ab955f43b3248d0
This commit is contained in:
Kiran Pawar 2022-03-29 17:29:01 +00:00
parent a1f006d799
commit 08e8bdf271
5 changed files with 35 additions and 9 deletions

View File

@ -2711,6 +2711,7 @@ class ComputeManager(manager.Manager):
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self._build_resources_cleanup(instance, network_info) self._build_resources_cleanup(instance, network_info)
except (exception.UnexpectedTaskStateError, except (exception.UnexpectedTaskStateError,
exception.InstanceUnacceptable,
exception.OverQuota, exception.InvalidBDM) as e: exception.OverQuota, exception.InvalidBDM) as e:
self._build_resources_cleanup(instance, network_info) self._build_resources_cleanup(instance, network_info)
raise exception.BuildAbortException(instance_uuid=instance.uuid, raise exception.BuildAbortException(instance_uuid=instance.uuid,

View File

@ -166,7 +166,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
"projectid:fake_project\n" "projectid:fake_project\n"
"projectname:None\n" "projectname:None\n"
"flavor:name:m1.micro\n" "flavor:name:m1.micro\n"
"flavor:memory_mb:6\n" "flavor:memory_mb:8\n"
"flavor:vcpus:28\n" "flavor:vcpus:28\n"
"flavor:ephemeral_gb:8128\n" "flavor:ephemeral_gb:8128\n"
"flavor:root_gb:496\n" "flavor:root_gb:496\n"
@ -1138,6 +1138,14 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
mock_attach_cdrom_to_vm.assert_called_once_with( mock_attach_cdrom_to_vm.assert_called_once_with(
vm_ref, self._instance, self._ds.ref, str(upload_iso_path)) vm_ref, self._instance, self._ds.ref, str(upload_iso_path))
def test_prepare_for_spawn_invalid_ram(self):
instance = self._instance.obj_clone()
flavor = objects.Flavor(vcpus=1, memory_mb=6, ephemeral_gb=1,
swap=1024, extra_specs={})
instance.flavor = flavor
self.assertRaises(exception.InstanceUnacceptable,
self._vmops.prepare_for_spawn, instance)
@mock.patch('nova.image.glance.API.get') @mock.patch('nova.image.glance.API.get')
@mock.patch.object(vmops.LOG, 'debug') @mock.patch.object(vmops.LOG, 'debug')
@mock.patch.object(vmops.VMwareVMOps, '_fetch_image_if_missing') @mock.patch.object(vmops.VMwareVMOps, '_fetch_image_if_missing')
@ -2176,7 +2184,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
def _validate_flavor_extra_specs(self, flavor_extra_specs, expected): def _validate_flavor_extra_specs(self, flavor_extra_specs, expected):
# Validate that the extra specs are parsed correctly # Validate that the extra specs are parsed correctly
flavor = objects.Flavor(name='my-flavor', flavor = objects.Flavor(name='my-flavor',
memory_mb=6, memory_mb=8,
vcpus=28, vcpus=28,
root_gb=496, root_gb=496,
ephemeral_gb=8128, ephemeral_gb=8128,
@ -2227,7 +2235,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
flavor_extra_specs = {'quota:cpu_limit': 7, flavor_extra_specs = {'quota:cpu_limit': 7,
'quota:cpu_reservation': 6} 'quota:cpu_reservation': 6}
flavor = objects.Flavor(name='my-flavor', flavor = objects.Flavor(name='my-flavor',
memory_mb=6, memory_mb=8,
vcpus=28, vcpus=28,
root_gb=496, root_gb=496,
ephemeral_gb=8128, ephemeral_gb=8128,
@ -2280,7 +2288,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
'quota:cpu_reservation': 6, 'quota:cpu_reservation': 6,
'hw_video:ram_max_mb': 100} 'hw_video:ram_max_mb': 100}
flavor = objects.Flavor(name='my-flavor', flavor = objects.Flavor(name='my-flavor',
memory_mb=6, memory_mb=8,
vcpus=28, vcpus=28,
root_gb=496, root_gb=496,
ephemeral_gb=8128, ephemeral_gb=8128,
@ -2692,7 +2700,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
def test_get_storage_policy_none(self): def test_get_storage_policy_none(self):
flavor = objects.Flavor(name='m1.small', flavor = objects.Flavor(name='m1.small',
memory_mb=6, memory_mb=8,
vcpus=28, vcpus=28,
root_gb=496, root_gb=496,
ephemeral_gb=8128, ephemeral_gb=8128,
@ -2706,7 +2714,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
def test_get_storage_policy_extra_specs(self): def test_get_storage_policy_extra_specs(self):
extra_specs = {'vmware:storage_policy': 'flavor-policy'} extra_specs = {'vmware:storage_policy': 'flavor-policy'}
flavor = objects.Flavor(name='m1.small', flavor = objects.Flavor(name='m1.small',
memory_mb=6, memory_mb=8,
vcpus=28, vcpus=28,
root_gb=496, root_gb=496,
ephemeral_gb=8128, ephemeral_gb=8128,
@ -2781,7 +2789,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
def test_get_instance_metadata(self): def test_get_instance_metadata(self):
flavor = objects.Flavor(id=7, flavor = objects.Flavor(id=7,
name='m1.small', name='m1.small',
memory_mb=6, memory_mb=8,
vcpus=28, vcpus=28,
root_gb=496, root_gb=496,
ephemeral_gb=8128, ephemeral_gb=8128,
@ -2796,7 +2804,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
"projectid:fake_project\n" "projectid:fake_project\n"
"projectname:None\n" "projectname:None\n"
"flavor:name:m1.small\n" "flavor:name:m1.small\n"
"flavor:memory_mb:6\n" "flavor:memory_mb:8\n"
"flavor:vcpus:28\n" "flavor:vcpus:28\n"
"flavor:ephemeral_gb:8128\n" "flavor:ephemeral_gb:8128\n"
"flavor:root_gb:496\n" "flavor:root_gb:496\n"
@ -2913,7 +2921,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
def test_get_cores_per_socket(self): def test_get_cores_per_socket(self):
extra_specs = {'hw:cpu_sockets': 7} extra_specs = {'hw:cpu_sockets': 7}
flavor = objects.Flavor(name='m1.small', flavor = objects.Flavor(name='m1.small',
memory_mb=6, memory_mb=8,
vcpus=28, vcpus=28,
root_gb=496, root_gb=496,
ephemeral_gb=8128, ephemeral_gb=8128,

View File

@ -519,6 +519,10 @@ class VMwareVCDriver(driver.ComputeDriver):
# where cpu traits are added. In the vmware world, this is where we # where cpu traits are added. In the vmware world, this is where we
# would add nested providers representing tenant VDC and similar. # would add nested providers representing tenant VDC and similar.
def prepare_for_spawn(self, instance):
"""Perform pre-checks for spawn."""
self._vmops.prepare_for_spawn(instance)
def spawn(self, context, instance, image_meta, injected_files, def spawn(self, context, instance, image_meta, injected_files,
admin_password, allocations, network_info=None, admin_password, allocations, network_info=None,
block_device_info=None, power_on=True, accel_info=None): block_device_info=None, power_on=True, accel_info=None):

View File

@ -729,6 +729,12 @@ class VMwareVMOps(object):
if new_size is not None: if new_size is not None:
vi.ii.file_size = new_size vi.ii.file_size = new_size
def prepare_for_spawn(self, instance):
if (int(instance.flavor.memory_mb) % 4 != 0):
reason = _("Memory size is not multiple of 4")
raise exception.InstanceUnacceptable(instance_id=instance.uuid,
reason=reason)
def spawn(self, context, instance, image_meta, injected_files, def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info, block_device_info=None): admin_password, network_info, block_device_info=None):

View File

@ -0,0 +1,7 @@
---
fixes:
- |
For the VMware ESXi, VM memory should be multiple of 4. Otherwise creating
instance on ESXi fails with error "VimFaultException: Memory (RAM) size is
invalid.". Instances will now fail to spawn if flavor memory is not a
multiple of 4.