Merge bdms of run instances parameters with image mappings
Implement AWS feature which allows a user to adjust bdm specified in an image. The feature is used with boot operation by specifying changed device attributes with the device name. Change-Id: Ifb9c215527cd84a60f21ab73fee672928b604300
This commit is contained in:
parent
476e899552
commit
ffc445c964
|
@ -887,6 +887,10 @@ def _parse_image_parameters(context, image_id, kernel_id, ramdisk_id):
|
|||
|
||||
def _parse_block_device_mapping(context, block_device_mapping):
|
||||
# TODO(ft): check block_device_mapping structure
|
||||
# TODO(ft): leave only the last bdm if several bdms are occured with
|
||||
# the same device name. If names differ in /dev/ prefix occurence,
|
||||
# raise InvalidBlockDeviceMapping with the message
|
||||
# The device '<short_name>' is used in more than one block-device mapping
|
||||
# TODO(ft): support virtual devices
|
||||
# TODO(ft): support no_device
|
||||
bdms = []
|
||||
|
@ -927,18 +931,67 @@ def _parse_block_device_mapping(context, block_device_mapping):
|
|||
def _build_block_device_mapping(context, block_device_mapping, os_image):
|
||||
mappings = _parse_block_device_mapping(context, block_device_mapping)
|
||||
properties = ec2utils.deserialize_os_image_properties(os_image)
|
||||
image_bdms = ec2utils.get_os_image_mappings(properties)
|
||||
root_device_name = (
|
||||
ec2utils.block_device_properties_root_device_name(properties))
|
||||
short_root_device_name = ec2utils.block_device_strip_dev(root_device_name)
|
||||
image_bdm_dict = {}
|
||||
for bdm in image_bdms:
|
||||
if bdm.get('device_name'):
|
||||
key = ec2utils.block_device_strip_dev(bdm['device_name'])
|
||||
if key == short_root_device_name:
|
||||
bdm.setdefault('boot_index', 0)
|
||||
elif bdm.get('boot_index') == 0:
|
||||
key = short_root_device_name
|
||||
bdm.setdefault('device_name', root_device_name)
|
||||
else:
|
||||
continue
|
||||
image_bdm_dict[key] = bdm
|
||||
result = []
|
||||
# NOTE(ft): only the last device definition in the list is considered
|
||||
# by AWS. So get only the last definitions, despite Nova can do the same.
|
||||
# Because this is not contracted in Nova.
|
||||
for bdm in mappings:
|
||||
_populate_parsed_bdm_parameter(bdm, short_root_device_name)
|
||||
short_device_name = ec2utils.block_device_strip_dev(bdm['device_name'])
|
||||
if short_device_name not in image_bdm_dict:
|
||||
_populate_parsed_bdm_parameter(bdm, short_root_device_name)
|
||||
else:
|
||||
image_bdm = image_bdm_dict[short_device_name]
|
||||
if bdm['device_name'] != image_bdm['device_name']:
|
||||
raise exception.InvalidBlockDeviceMapping(
|
||||
_("The device '%s' is used in more than one "
|
||||
"block-device mapping") % short_device_name)
|
||||
if (image_bdm.get('boot_index') == 0 and 'snapshot_id' in bdm and
|
||||
bdm['snapshot_id'] != image_bdm.get('snapshot_id')):
|
||||
raise exception.InvalidBlockDeviceMapping(
|
||||
_('snapshotId cannot be modified on root device'))
|
||||
if ('volume_size' in bdm and 'volume_size' in image_bdm and
|
||||
bdm['volume_size'] < image_bdm['volume_size']):
|
||||
raise exception.InvalidBlockDeviceMapping(
|
||||
_("Volume of size %(bdm_size)dGB is smaller than expected "
|
||||
"size %(image_bdm_size)dGB for '(device_name)s'") %
|
||||
{'bdm_size': bdm['volume_size'],
|
||||
'image_bdm_size': image_bdm['volume_size'],
|
||||
'device_name': bdm['device_name']})
|
||||
|
||||
if bdm.get('snapshot_id'):
|
||||
if 'snapshot_id' not in image_bdm:
|
||||
raise exception.InvalidBlockDeviceMapping(
|
||||
_('snapshotId can only be modified on EBS devices'))
|
||||
|
||||
_populate_parsed_bdm_parameter(bdm, short_root_device_name)
|
||||
else:
|
||||
image_bdm.update(bdm)
|
||||
bdm = image_bdm
|
||||
|
||||
source_type = bdm.get('source_type')
|
||||
if source_type:
|
||||
uuid = bdm.pop('_'.join([source_type, 'id']), None)
|
||||
if uuid:
|
||||
bdm['uuid'] = uuid
|
||||
|
||||
result.append(bdm)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
@ -239,6 +239,10 @@ class InvalidSnapshotIDMalformed(EC2InvalidException):
|
|||
msg_fmg = _('The snapshot %(id)s ID is not valid')
|
||||
|
||||
|
||||
class InvalidBlockDeviceMapping(EC2InvalidException):
|
||||
pass
|
||||
|
||||
|
||||
class IncorrectState(EC2IncorrectStateException):
|
||||
msg_fmt = _("The resource is in incorrect state for the request - reason: "
|
||||
"'%(reason)s'")
|
||||
|
|
|
@ -1465,8 +1465,10 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
|
|||
def test_build_block_device_mapping(self, db_api):
|
||||
fake_context = mock.Mock(service_catalog=[{'type': 'fake'}])
|
||||
db_api.get_item_by_id.side_effect = tools.get_db_api_get_item_by_id(
|
||||
fakes.DB_SNAPSHOT_1, fakes.DB_VOLUME_1)
|
||||
fakes.DB_SNAPSHOT_1, fakes.DB_SNAPSHOT_2,
|
||||
fakes.DB_VOLUME_1, fakes.DB_VOLUME_2)
|
||||
|
||||
# check bdm attributes' population
|
||||
bdms = [
|
||||
{'device_name': '/dev/sda1',
|
||||
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_1}},
|
||||
|
@ -1496,11 +1498,217 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
|
|||
'delete_on_termination': True,
|
||||
'boot_index': -1},
|
||||
]
|
||||
|
||||
result = instance_api._build_block_device_mapping(
|
||||
fake_context, bdms, fakes.OSImage(fakes.OS_IMAGE_1))
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
fake_image_template = {
|
||||
'id': fakes.random_os_id(),
|
||||
'properties': {'root_device_name': '/dev/vda',
|
||||
'bdm_v2': True,
|
||||
'block_device_mapping': []}}
|
||||
|
||||
# check merging with image bdms
|
||||
fake_image_template['properties']['block_device_mapping'] = [
|
||||
{'boot_index': 0,
|
||||
'device_name': '/dev/vda',
|
||||
'source_type': 'snapshot',
|
||||
'snapshot_id': fakes.ID_OS_SNAPSHOT_1,
|
||||
'delete_on_termination': True},
|
||||
{'device_name': 'vdb',
|
||||
'source_type': 'snapshot',
|
||||
'snapshot_id': fakes.random_os_id(),
|
||||
'volume_size': 50},
|
||||
{'device_name': '/dev/vdc',
|
||||
'source_type': 'blank',
|
||||
'volume_size': 10},
|
||||
]
|
||||
bdms = [
|
||||
{'device_name': '/dev/vda',
|
||||
'ebs': {'volume_size': 15}},
|
||||
{'device_name': 'vdb',
|
||||
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_2,
|
||||
'delete_on_termination': False}},
|
||||
{'device_name': '/dev/vdc',
|
||||
'ebs': {'volume_size': 20}},
|
||||
]
|
||||
expected = [
|
||||
{'device_name': '/dev/vda',
|
||||
'source_type': 'snapshot',
|
||||
'destination_type': 'volume',
|
||||
'uuid': fakes.ID_OS_SNAPSHOT_1,
|
||||
'delete_on_termination': True,
|
||||
'volume_size': 15,
|
||||
'boot_index': 0},
|
||||
{'device_name': 'vdb',
|
||||
'source_type': 'snapshot',
|
||||
'destination_type': 'volume',
|
||||
'uuid': fakes.ID_OS_SNAPSHOT_2,
|
||||
'delete_on_termination': False,
|
||||
'boot_index': -1},
|
||||
{'device_name': '/dev/vdc',
|
||||
'source_type': 'blank',
|
||||
'destination_type': 'volume',
|
||||
'volume_size': 20,
|
||||
'delete_on_termination': False},
|
||||
]
|
||||
result = instance_api._build_block_device_mapping(
|
||||
fake_context, bdms, fakes.OSImage(fake_image_template))
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
# check result order for adjusting some bdm of all
|
||||
fake_image_template['properties']['block_device_mapping'] = [
|
||||
{'device_name': '/dev/vdc',
|
||||
'source_type': 'blank',
|
||||
'volume_size': 10},
|
||||
{'device_name': '/dev/vde',
|
||||
'source_type': 'blank',
|
||||
'volume_size': 10},
|
||||
{'device_name': '/dev/vdf',
|
||||
'source_type': 'blank',
|
||||
'volume_size': 10},
|
||||
{'boot_index': -1,
|
||||
'source_type': 'blank',
|
||||
'volume_size': 10},
|
||||
]
|
||||
bdms = [
|
||||
{'device_name': '/dev/vdh',
|
||||
'ebs': {'volume_size': 15}},
|
||||
{'device_name': '/dev/vde',
|
||||
'ebs': {'volume_size': 15}},
|
||||
{'device_name': '/dev/vdb',
|
||||
'ebs': {'volume_size': 15}},
|
||||
]
|
||||
expected = [
|
||||
{'device_name': '/dev/vdh',
|
||||
'source_type': 'blank',
|
||||
'destination_type': 'volume',
|
||||
'volume_size': 15,
|
||||
'delete_on_termination': True,
|
||||
'boot_index': -1},
|
||||
{'device_name': '/dev/vde',
|
||||
'source_type': 'blank',
|
||||
'destination_type': 'volume',
|
||||
'volume_size': 15,
|
||||
'delete_on_termination': False},
|
||||
{'device_name': '/dev/vdb',
|
||||
'source_type': 'blank',
|
||||
'destination_type': 'volume',
|
||||
'volume_size': 15,
|
||||
'delete_on_termination': True,
|
||||
'boot_index': -1},
|
||||
]
|
||||
result = instance_api._build_block_device_mapping(
|
||||
fake_context, bdms, fakes.OSImage(fake_image_template))
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
# check conflict of short and full device names
|
||||
fake_image_template['properties']['block_device_mapping'] = [
|
||||
{'device_name': '/dev/vdc',
|
||||
'source_type': 'blank',
|
||||
'volume_size': 10},
|
||||
]
|
||||
bdms = [
|
||||
{'device_name': 'vdc',
|
||||
'ebs': {'volume_size': 15}},
|
||||
]
|
||||
self.assertRaises(exception.InvalidBlockDeviceMapping,
|
||||
instance_api._build_block_device_mapping,
|
||||
fake_context, bdms,
|
||||
fakes.OSImage(fake_image_template))
|
||||
|
||||
# opposit combination of the same case
|
||||
fake_image_template['properties']['block_device_mapping'] = [
|
||||
{'device_name': 'vdc',
|
||||
'source_type': 'blank',
|
||||
'volume_size': 10},
|
||||
]
|
||||
bdms = [
|
||||
{'device_name': '/dev/vdc',
|
||||
'ebs': {'volume_size': 15}},
|
||||
]
|
||||
self.assertRaises(exception.InvalidBlockDeviceMapping,
|
||||
instance_api._build_block_device_mapping,
|
||||
fake_context, bdms,
|
||||
fakes.OSImage(fake_image_template))
|
||||
|
||||
# check fault on root device snapshot changing
|
||||
fake_image_template['properties']['block_device_mapping'] = [
|
||||
{'boot_index': 0,
|
||||
'source_type': 'snapshot',
|
||||
'snapshot_id': fakes.ID_EC2_SNAPSHOT_1},
|
||||
]
|
||||
bdms = [
|
||||
{'device_name': '/dev/vda',
|
||||
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_2}},
|
||||
]
|
||||
self.assertRaises(exception.InvalidBlockDeviceMapping,
|
||||
instance_api._build_block_device_mapping,
|
||||
fake_context, bdms,
|
||||
fakes.OSImage(fake_image_template))
|
||||
|
||||
# same case for legacy bdm
|
||||
fake_image_template['properties']['block_device_mapping'] = [
|
||||
{'device_name': '/dev/vda',
|
||||
'snapshot_id': fakes.ID_EC2_SNAPSHOT_1},
|
||||
]
|
||||
fake_image_template['properties']['bdm_v2'] = False
|
||||
bdms = [
|
||||
{'device_name': '/dev/vda',
|
||||
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_2}},
|
||||
]
|
||||
self.assertRaises(exception.InvalidBlockDeviceMapping,
|
||||
instance_api._build_block_device_mapping,
|
||||
fake_context, bdms,
|
||||
fakes.OSImage(fake_image_template))
|
||||
|
||||
# same case for legacy bdm with short names
|
||||
fake_image_template['properties']['block_device_mapping'] = [
|
||||
{'device_name': 'vda',
|
||||
'snapshot_id': fakes.ID_EC2_SNAPSHOT_1},
|
||||
]
|
||||
fake_image_template['properties']['bdm_v2'] = False
|
||||
bdms = [
|
||||
{'device_name': 'vda',
|
||||
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_2}},
|
||||
]
|
||||
self.assertRaises(exception.InvalidBlockDeviceMapping,
|
||||
instance_api._build_block_device_mapping,
|
||||
fake_context, bdms,
|
||||
fakes.OSImage(fake_image_template))
|
||||
|
||||
fake_image_template['properties']['bdm_v2'] = True
|
||||
|
||||
# check fault on reduce volume size
|
||||
fake_image_template['properties']['block_device_mapping'] = [
|
||||
{'device_name': 'vdc',
|
||||
'source_type': 'blank',
|
||||
'volume_size': 15},
|
||||
]
|
||||
bdms = [
|
||||
{'device_name': '/dev/vdc',
|
||||
'ebs': {'volume_size': 10}},
|
||||
]
|
||||
self.assertRaises(exception.InvalidBlockDeviceMapping,
|
||||
instance_api._build_block_device_mapping,
|
||||
fake_context, bdms,
|
||||
fakes.OSImage(fake_image_template))
|
||||
|
||||
# check fault on set snapshot id if bdm doesn't have one
|
||||
fake_image_template['properties']['block_device_mapping'] = [
|
||||
{'device_name': 'vdc',
|
||||
'source_type': 'blank',
|
||||
'volume_size': 10},
|
||||
]
|
||||
bdms = [
|
||||
{'device_name': '/dev/vdc',
|
||||
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_1}},
|
||||
]
|
||||
self.assertRaises(exception.InvalidBlockDeviceMapping,
|
||||
instance_api._build_block_device_mapping,
|
||||
fake_context, bdms,
|
||||
fakes.OSImage(fake_image_template))
|
||||
|
||||
@mock.patch('cinderclient.client.Client')
|
||||
@mock.patch('novaclient.client.Client')
|
||||
@mock.patch('ec2api.db.api.IMPL')
|
||||
|
|
Loading…
Reference in New Issue