diff --git a/nova/compute/api.py b/nova/compute/api.py index 0e77b16b763d..9da39f3b4754 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -879,15 +879,13 @@ class API(base.Base): @staticmethod def _volume_size(instance_type, bdm): - size = 0 - if bdm.get('source_type') == 'blank': + size = bdm.get('volume_size') + if size is None and bdm.get('source_type') == 'blank': if bdm.get('guest_format') == 'swap': size = instance_type.get('swap', 0) else: size = instance_type.get('ephemeral_gb', 0) - return size - else: - return bdm.get('volume_size') + return size def _prepare_image_mapping(self, instance_type, instance_uuid, mappings): """Extract and format blank devices from image mappings.""" @@ -937,11 +935,7 @@ class API(base.Base): LOG.debug(_("block_device_mapping %s"), block_device_mapping, instance_uuid=instance_uuid) for bdm in block_device_mapping: - # Default the size of blank devices per instance type - dest_type = bdm.get('destination_type') - if dest_type == 'blank': - bdm['volume_size'] = self._volume_size( - instance_type, bdm) + bdm['volume_size'] = self._volume_size(instance_type, bdm) if bdm.get('volume_size') == 0: continue @@ -991,6 +985,24 @@ class API(base.Base): except Exception: raise exception.InvalidBDMSnapshot(id=snapshot_id) + ephemeral_size = sum(bdm.get('volume_size') or 0 + for bdm in all_mappings + if block_device.new_format_is_ephemeral(bdm)) + if ephemeral_size > instance_type['ephemeral_gb']: + raise exception.InvalidBDMEphemeralSize() + + # There should be only one swap + swap_list = [bdm for bdm in all_mappings + if block_device.new_format_is_swap(bdm)] + if len(swap_list) > 1: + msg = _("More than one swap drive requested.") + raise exception.InvalidBDMFormat(details=msg) + + if swap_list: + swap_size = swap_list[0].get('volume_size') or 0 + if swap_size > instance_type['swap']: + raise exception.InvalidBDMSwapSize() + max_local = CONF.max_local_block_devices if max_local >= 0: num_local = len([bdm for bdm in all_mappings diff --git a/nova/exception.py b/nova/exception.py index de36a663628c..0830083ad0b5 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -235,6 +235,15 @@ class InvalidBDMLocalsLimit(InvalidBDM): "limit allows") +class InvalidBDMEphemeralSize(InvalidBDM): + msg_fmt = _("Ephemeral disks requested are larger than " + "the instance type allows.") + + +class InvalidBDMSwapSize(InvalidBDM): + msg_fmt = _("Swap drive requested is larger than instance type allows.") + + class InvalidBDMFormat(InvalidBDM): msg_fmt = _("Block Device Mapping is Invalid: " "%(details)s") diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index a6c21b5287c6..d0b334dcf8ee 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -686,7 +686,7 @@ class ComputeVolumeTestCase(BaseTestCase): snapshot_id = '66666666-aaaa-bbbb-cccc-555555555555' instance = self._create_fake_instance() - instance_type = {'swap': 1, 'ephemeral_gb': 1} + instance_type = {'swap': 1, 'ephemeral_gb': 2} mappings = [ { 'device_name': '/dev/sdb4', @@ -746,6 +746,68 @@ class ComputeVolumeTestCase(BaseTestCase): self.compute_api._validate_bdm, self.context, instance, instance_type, mappings) + ephemerals = [ + { + 'device_name': '/dev/vdb', + 'source_type': 'blank', + 'destination_type': 'local', + 'device_type': 'disk', + 'volume_id': volume_id, + 'guest_format': None, + 'boot_index': -1, + 'volume_size': 1 + }, + { + 'device_name': '/dev/vdc', + 'source_type': 'blank', + 'destination_type': 'local', + 'device_type': 'disk', + 'volume_id': volume_id, + 'guest_format': None, + 'boot_index': -1, + 'volume_size': 1 + }] + + self.flags(max_local_block_devices=4) + # More ephemerals are OK as long as they are not over the size limit + self.compute_api._validate_bdm(self.context, instance, + instance_type, mappings + ephemerals) + + # Ephemerals over the size limit + ephemerals[0]['volume_size'] = 3 + self.assertRaises(exception.InvalidBDMEphemeralSize, + self.compute_api._validate_bdm, + self.context, instance, instance_type, + mappings + ephemerals) + self.assertRaises(exception.InvalidBDMEphemeralSize, + self.compute_api._validate_bdm, + self.context, instance, instance_type, + mappings + [ephemerals[0]]) + + # Swap over the size limit + mappings[0]['volume_size'] = 3 + self.assertRaises(exception.InvalidBDMSwapSize, + self.compute_api._validate_bdm, + self.context, instance, instance_type, + mappings) + mappings[0]['volume_size'] = 1 + + additional_swap = [ + { + 'device_name': '/dev/vdb', + 'source_type': 'blank', + 'destination_type': 'local', + 'device_type': 'disk', + 'guest_format': 'swap', + 'boot_index': -1, + 'volume_size': 1 + }] + + # More than one swap + self.assertRaises(exception.InvalidBDMFormat, + self.compute_api._validate_bdm, + self.context, instance, instance_type, + mappings + additional_swap) def test_validate_bdm_media_service_exceptions(self): instance_type = {'swap': 1, 'ephemeral_gb': 1} @@ -6710,9 +6772,15 @@ class ComputeAPITestCase(BaseTestCase): self.assertEqual( self.compute_api._volume_size(inst_type, ephemeral_bdm), ephemeral_size) + ephemeral_bdm['volume_size'] = 42 + self.assertEqual( + self.compute_api._volume_size(inst_type, ephemeral_bdm), 42) self.assertEqual( self.compute_api._volume_size(inst_type, swap_bdm), swap_size) + swap_bdm['volume_size'] = 42 + self.assertEqual( + self.compute_api._volume_size(inst_type, swap_bdm), 42) self.assertEqual( self.compute_api._volume_size(inst_type, volume_bdm), volume_size)