Release bdm constraint source and dest type

https://bugs.launchpad.net/nova/+bug/1377958 fixed a problem
that source_type: image, destination_type: local is not
supported for boot instance, exception should be raised to
reject the param otherwise it will lead to instance become
ERROR state.

However the fix introduced a problem on nova client
https://bugs.launchpad.net/python-novaclient/+bug/1418484
The fix of the bug leads to following command become invalid

nova boot test-vm --flavor m1.medium --image centos-vm-32
--nic net-id=c3f40e33-d535-4217-916b-1450b8cd3987 --block-device
id=26b7b917-2794-452a-95e5-2efb2ca6e32d,bus=sata,source=volume,bootindex=1

So we need to release the original constraint to allow
the above special case pass the validation check then
we can revert the nova client exception
(https://review.openstack.org/#/c/165932/)

This patch checks the boot_index and whether image param is
given after we found the bdm has source_type: image,
destination_type: local, if this is the special case, then
no exception will be raised.

Closes-Bug: #1433609

Change-Id: If43faae95169bc3864449a8364975f5c887aac14
This commit is contained in:
jichenjc 2015-03-20 08:36:37 +08:00
parent 37a6c601f3
commit cadbcc440a
4 changed files with 45 additions and 10 deletions

View File

@ -47,6 +47,11 @@ class BlockDeviceMapping(extensions.V3APIExtensionBase):
# NOTE(gmann): This function is not supposed to use 'body_deprecated_param'
# parameter as this is placed to handle scheduler_hint extension for V2.1.
def server_create(self, server_dict, create_kwargs, body_deprecated_param):
# Have to check whether --image is given, see bug 1433609
image_href = server_dict.get('imageRef')
image_uuid_specified = image_href is not None
bdm = server_dict.get(ATTRIBUTE_NAME, [])
legacy_bdm = server_dict.get(LEGACY_ATTRIBUTE_NAME, [])
@ -57,7 +62,8 @@ class BlockDeviceMapping(extensions.V3APIExtensionBase):
try:
block_device_mapping = [
block_device.BlockDeviceDict.from_api(bdm_dict)
block_device.BlockDeviceDict.from_api(bdm_dict,
image_uuid_specified)
for bdm_dict in bdm]
except exception.InvalidBDMFormat as e:
raise exc.HTTPBadRequest(explanation=e.format_message())

View File

@ -383,7 +383,7 @@ class Controller(wsgi.Controller):
raise exc.HTTPBadRequest(explanation=expl)
return user_data
def _extract_bdm(self, server_dict):
def _extract_bdm(self, server_dict, image_uuid_specified):
legacy_bdm = True
block_device_mapping = None
block_device_mapping_v2 = None
@ -424,7 +424,8 @@ class Controller(wsgi.Controller):
try:
block_device_mapping_v2 = [
block_device.BlockDeviceDict.from_api(bdm_dict)
block_device.BlockDeviceDict.from_api(bdm_dict,
image_uuid_specified)
for bdm_dict in block_device_mapping_v2]
except exception.InvalidBDMFormat as e:
raise exc.HTTPBadRequest(explanation=e.format_message())
@ -543,7 +544,9 @@ class Controller(wsgi.Controller):
user_data = self._extract(server_dict, 'os-user-data', 'user_data')
self._validate_user_data(user_data)
legacy_bdm, block_device_mapping = self._extract_bdm(server_dict)
image_uuid_specified = bool(image_uuid)
legacy_bdm, block_device_mapping = self._extract_bdm(server_dict,
image_uuid_specified)
ret_resv_id = False
# min_count and max_count are optional. If they exist, they may come

View File

@ -171,7 +171,7 @@ class BlockDeviceDict(dict):
return cls(new_bdm, non_computable_fields)
@classmethod
def from_api(cls, api_dict):
def from_api(cls, api_dict, image_uuid_specified):
"""Transform the API format of data to the internally used one.
Only validate if the source_type field makes sense.
@ -194,8 +194,13 @@ class BlockDeviceDict(dict):
details=_("Missing device UUID."))
api_dict[source_type + '_id'] = device_uuid
if source_type == 'image' and destination_type == 'local':
raise exception.InvalidBDMFormat(
details=_("Mapping image to local is not supported."))
boot_index = api_dict.get('boot_index', -1)
# if this bdm is generated from --image ,then
# source_type = image and destination_type = local is allowed
if not (image_uuid_specified and boot_index == 0):
raise exception.InvalidBDMFormat(
details=_("Mapping image to local is not supported."))
api_dict.pop('uuid', None)
return cls(api_dict)

View File

@ -523,7 +523,7 @@ class TestBlockDeviceDict(test.NoDBTestCase):
if new['snapshot_id']:
new['volume_id'] = None
self.assertThat(
block_device.BlockDeviceDict.from_api(api),
block_device.BlockDeviceDict.from_api(api, False),
matchers.IsSubDictOf(new))
def test_from_api_invalid_blank_id(self):
@ -534,7 +534,8 @@ class TestBlockDeviceDict(test.NoDBTestCase):
'delete_on_termination': True,
'boot_index': -1}
self.assertRaises(exception.InvalidBDMFormat,
block_device.BlockDeviceDict.from_api, api_dict)
block_device.BlockDeviceDict.from_api, api_dict,
False)
def test_from_api_invalid_source_to_local_mapping(self):
api_dict = {'id': 1,
@ -542,7 +543,27 @@ class TestBlockDeviceDict(test.NoDBTestCase):
'destination_type': 'local',
'uuid': 'fake-volume-id-1'}
self.assertRaises(exception.InvalidBDMFormat,
block_device.BlockDeviceDict.from_api, api_dict)
block_device.BlockDeviceDict.from_api, api_dict,
False)
def test_from_api_valid_source_to_local_mapping(self):
api_dict = {'id': 1,
'source_type': 'image',
'destination_type': 'local',
'volume_id': 'fake-volume-id-1',
'uuid': 1,
'boot_index': 0}
retexp = block_device.BlockDeviceDict(
{'id': 1,
'source_type': 'image',
'image_id': 1,
'destination_type': 'local',
'volume_id': 'fake-volume-id-1',
'boot_index': 0})
self.assertEqual(
block_device.BlockDeviceDict.from_api(api_dict, True),
retexp)
def test_legacy(self):
for legacy, new in zip(self.legacy_mapping, self.new_mapping):