From fcf712ec3538d4e07e0c0da6fadcd4f2ea7747fc Mon Sep 17 00:00:00 2001 From: Nikola Dipanov Date: Fri, 30 Aug 2013 16:40:43 +0200 Subject: [PATCH] Check ephemeral and swap size in the API Validate that ephemeral and swap disks passed in trough the API are within size limits for the given instance type. The validation is done in the API layer. Closes-bug #1218977 Change-Id: I96c6e651e4b221313c39dfc41e79d536585fb955 --- nova/compute/api.py | 32 +++++++++----- nova/exception.py | 9 ++++ nova/tests/compute/test_compute.py | 70 +++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 11 deletions(-) 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)