diff --git a/nova/compute/api.py b/nova/compute/api.py index 5b4386d47a..871043f8c7 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -616,50 +616,58 @@ class API(base.Base): if int(image.get('min_disk') or 0) > root_gb: raise exception.FlavorDiskTooSmall() - def _check_and_transform_bdm(self, base_options, image_meta, min_count, - max_count, block_device_mapping, legacy_bdm): + def _get_image_defined_bdms(self, base_options, instance_type, image_meta, + root_device_name): + image_properties = image_meta.get('properties', {}) + + # Get the block device mappings defined by the image. + image_defined_bdms = image_properties.get('block_device_mapping', []) + legacy_image_defined = not image_properties.get('bdm_v2', False) + + image_mapping = image_properties.get('mappings', []) + + if legacy_image_defined: + image_defined_bdms = block_device.from_legacy_mapping( + image_defined_bdms, None, root_device_name) + else: + image_defined_bdms = map(block_device.BlockDeviceDict, + image_defined_bdms) + + if image_mapping: + image_defined_bdms += self._prepare_image_mapping( + instance_type, image_mapping) + + return image_defined_bdms + + def _check_and_transform_bdm(self, base_options, instance_type, image_meta, + min_count, max_count, block_device_mapping, + legacy_bdm): # NOTE (ndipanov): Assume root dev name is 'vda' if not supplied. # It's needed for legacy conversion to work. root_device_name = (base_options.get('root_device_name') or 'vda') image_ref = base_options.get('image_ref', '') - # Get the block device mappings defined by the image. - image_defined_bdms = \ - image_meta.get('properties', {}).get('block_device_mapping', []) - legacy_image_defined = not image_meta.get( - 'properties', {}).get('bdm_v2', False) - - if not legacy_image_defined: - image_defined_bdms = map(block_device.BlockDeviceDict, - image_defined_bdms) + image_defined_bdms = self._get_image_defined_bdms( + base_options, instance_type, image_meta, root_device_name) + root_in_image_bdms = ( + block_device.get_root_bdm(image_defined_bdms) is not None) if legacy_bdm: - if legacy_image_defined: - block_device_mapping += image_defined_bdms - block_device_mapping = block_device.from_legacy_mapping( - block_device_mapping, image_ref, root_device_name) - else: - root_in_image_bdms = block_device.get_root_bdm( - image_defined_bdms) is not None - block_device_mapping = block_device.from_legacy_mapping( - block_device_mapping, image_ref, root_device_name, - no_root=root_in_image_bdms) + image_defined_bdms - else: + block_device_mapping = block_device.from_legacy_mapping( + block_device_mapping, image_ref, root_device_name, + no_root=root_in_image_bdms) + elif image_ref and root_in_image_bdms: # NOTE (ndipanov): client will insert an image mapping into the v2 # block_device_mapping, but if there is a bootable device in image # mappings - we need to get rid of the inserted image. - if legacy_image_defined: - image_defined_bdms = block_device.from_legacy_mapping( - image_defined_bdms, None, root_device_name) - root_in_image_bdms = block_device.get_root_bdm( - image_defined_bdms) is not None - if image_ref and root_in_image_bdms: - block_device_mapping = [bdm for bdm in block_device_mapping - if not ( - bdm.get('source_type') == 'image' - and bdm.get('boot_index') == 0)] + def not_image_and_root_bdm(bdm): + return not (bdm.get('boot_index') == 0 and + bdm.get('source_type') == 'image') - block_device_mapping += image_defined_bdms + block_device_mapping = ( + filter(not_image_and_root_bdm, block_device_mapping)) + + block_device_mapping += image_defined_bdms if min_count > 1 or max_count > 1: if any(map(lambda bdm: bdm['source_type'] == 'volume', @@ -980,7 +988,7 @@ class API(base.Base): max_count = max_net_count block_device_mapping = self._check_and_transform_bdm( - base_options, boot_meta, min_count, max_count, + base_options, instance_type, boot_meta, min_count, max_count, block_device_mapping, legacy_bdm) instances = self._provision_instances(context, instance_type, @@ -1018,13 +1026,13 @@ class API(base.Base): size = instance_type.get('ephemeral_gb', 0) return size - def _prepare_image_mapping(self, instance_type, instance_uuid, mappings): + def _prepare_image_mapping(self, instance_type, mappings): """Extract and format blank devices from image mappings.""" prepared_mappings = [] for bdm in block_device.mappings_prepend_dev(mappings): - LOG.debug(_("Image bdm %s"), bdm, instance_uuid=instance_uuid) + LOG.debug(_("Image bdm %s"), bdm) virtual_name = bdm['virtual'] if virtual_name == 'ami' or virtual_name == 'root': @@ -1147,25 +1155,6 @@ class API(base.Base): if num_local > max_local: raise exception.InvalidBDMLocalsLimit() - def _populate_instance_for_bdm(self, context, instance, instance_type, - image, block_device_mapping): - """Populate instance block device mapping information.""" - instance_uuid = instance['uuid'] - image_properties = image.get('properties', {}) - image_mapping = image_properties.get('mappings', []) - if image_mapping: - image_mapping = self._prepare_image_mapping(instance_type, - instance_uuid, image_mapping) - - self._validate_bdm(context, instance, instance_type, - block_device_mapping + image_mapping) - - for mapping in (image_mapping, block_device_mapping): - if not mapping: - continue - self._update_block_device_mapping(context, - instance_type, instance_uuid, mapping) - def _populate_instance_shutdown_terminate(self, instance, image, block_device_mapping): """Populate instance shutdown_terminate information.""" @@ -1271,12 +1260,15 @@ class API(base.Base): # has been created, so delete it and re-raise so # that other cleanup can happen. try: - self._populate_instance_for_bdm(context, instance, - instance_type, image, block_device_mapping) + self._validate_bdm( + context, instance, instance_type, block_device_mapping) except exception.InvalidBDM: with excutils.save_and_reraise_exception(): instance.destroy(context) + self._update_block_device_mapping( + context, instance_type, instance['uuid'], block_device_mapping) + return instance def _check_create_policies(self, context, availability_zone, diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index a61a45fcc7..d8ed1c45fc 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -765,7 +765,6 @@ class ComputeVolumeTestCase(BaseTestCase): ephemeral_size = 1 instance_type = {'swap': swap_size, 'ephemeral_gb': ephemeral_size} - instance = self._create_fake_instance() mappings = [ {'virtual': 'ami', 'device': 'sda1'}, {'virtual': 'root', 'device': '/dev/sda1'}, @@ -777,7 +776,7 @@ class ComputeVolumeTestCase(BaseTestCase): ] preped_bdm = self.compute_api._prepare_image_mapping( - instance_type, instance['uuid'], mappings) + instance_type, mappings) expected_result = [ { @@ -7953,7 +7952,7 @@ class ComputeAPITestCase(BaseTestCase): 'no_device': True}] image_mapping = self.compute_api._prepare_image_mapping( - instance_type, instance['uuid'], mappings) + instance_type, mappings) self.compute_api._update_block_device_mapping( self.context, instance_type, instance['uuid'], image_mapping) @@ -8052,7 +8051,7 @@ class ComputeAPITestCase(BaseTestCase): base_options = base_options or {'root_device_name': 'vda', 'image_ref': FAKE_IMAGE_REF} transformed_bdm = self.compute_api._check_and_transform_bdm( - base_options, image_meta, 1, 1, bdms, legacy_bdms) + base_options, {}, image_meta, 1, 1, bdms, legacy_bdms) self.assertThat(expected_bdms, matchers.DictListMatches(transformed_bdm)) @@ -8152,27 +8151,27 @@ class ComputeAPITestCase(BaseTestCase): # We get an image BDM transformed_bdm = self.compute_api._check_and_transform_bdm( - base_options, {}, 1, 1, fake_legacy_bdms, True) + base_options, {}, {}, 1, 1, fake_legacy_bdms, True) self.assertEqual(len(transformed_bdm), 2) # No image BDM created if image already defines a root BDM base_options['root_device_name'] = 'vda' transformed_bdm = self.compute_api._check_and_transform_bdm( - base_options, image_meta, 1, 1, [], True) + base_options, {}, image_meta, 1, 1, [], True) self.assertEqual(len(transformed_bdm), 1) # No image BDM created transformed_bdm = self.compute_api._check_and_transform_bdm( - base_options, {}, 1, 1, fake_legacy_bdms, True) + base_options, {}, {}, 1, 1, fake_legacy_bdms, True) self.assertEqual(len(transformed_bdm), 1) # Volumes with multiple instances fails self.assertRaises(exception.InvalidRequest, self.compute_api._check_and_transform_bdm, - base_options, {}, 1, 2, fake_legacy_bdms, True) + base_options, {}, {}, 1, 2, fake_legacy_bdms, True) checked_bdm = self.compute_api._check_and_transform_bdm( - base_options, {}, 1, 1, transformed_bdm, True) + base_options, {}, {}, 1, 1, transformed_bdm, True) self.assertEqual(checked_bdm, transformed_bdm) def test_volume_size(self):