Merge "compute: Extract _get_bdm_image_metadata into nova.utils"

This commit is contained in:
Zuul 2020-04-11 11:31:46 +00:00 committed by Gerrit Code Review
commit 065fc9e981
5 changed files with 94 additions and 72 deletions

View File

@ -1382,53 +1382,6 @@ class API(base.Base):
return certs_to_return
def _get_bdm_image_metadata(self, context, block_device_mapping,
legacy_bdm=True):
"""If we are booting from a volume, we need to get the
volume details from Cinder and make sure we pass the
metadata back accordingly.
"""
if not block_device_mapping:
return {}
for bdm in block_device_mapping:
if (legacy_bdm and
block_device.get_device_letter(
bdm.get('device_name', '')) != 'a'):
continue
elif not legacy_bdm and bdm.get('boot_index') != 0:
continue
volume_id = bdm.get('volume_id')
snapshot_id = bdm.get('snapshot_id')
if snapshot_id:
# NOTE(alaski): A volume snapshot inherits metadata from the
# originating volume, but the API does not expose metadata
# on the snapshot itself. So we query the volume for it below.
snapshot = self.volume_api.get_snapshot(context, snapshot_id)
volume_id = snapshot['volume_id']
if bdm.get('image_id'):
try:
image_id = bdm['image_id']
image_meta = self.image_api.get(context, image_id)
return image_meta
except Exception:
raise exception.InvalidBDMImage(id=image_id)
elif volume_id:
try:
volume = self.volume_api.get(context, volume_id)
except exception.CinderConnectionFailed:
raise
except Exception:
raise exception.InvalidBDMVolume(id=volume_id)
if not volume.get('bootable', True):
raise exception.InvalidBDMVolumeNotBootable(id=volume_id)
return utils.get_image_metadata_from_volume(volume)
return {}
@staticmethod
def _get_requested_instance_group(context, filter_properties):
if (not filter_properties or
@ -1481,8 +1434,9 @@ class API(base.Base):
"when booting from volume")
raise exception.CertificateValidationFailed(message=msg)
image_id = None
boot_meta = self._get_bdm_image_metadata(
context, block_device_mapping, legacy_bdm)
boot_meta = utils.get_bdm_image_metadata(
context, self.image_api, self.volume_api, block_device_mapping,
legacy_bdm)
self._check_auto_disk_config(image=boot_meta,
auto_disk_config=auto_disk_config)

View File

@ -4685,7 +4685,7 @@ class ServersControllerCreateTest(test.TestCase):
@mock.patch('nova.compute.api.API._get_volumes_for_bdms')
@mock.patch.object(compute_api.API, '_validate_bdm')
@mock.patch.object(compute_api.API, '_get_bdm_image_metadata')
@mock.patch('nova.utils.get_bdm_image_metadata')
def test_create_instance_with_bdms_and_no_image(
self, mock_bdm_image_metadata, mock_validate_bdm, mock_get_vols):
mock_bdm_image_metadata.return_value = {}
@ -4706,11 +4706,11 @@ class ServersControllerCreateTest(test.TestCase):
mock_validate_bdm.assert_called_once()
mock_bdm_image_metadata.assert_called_once_with(
mock.ANY, mock.ANY, False)
mock.ANY, mock.ANY, mock.ANY, mock.ANY, False)
@mock.patch('nova.compute.api.API._get_volumes_for_bdms')
@mock.patch.object(compute_api.API, '_validate_bdm')
@mock.patch.object(compute_api.API, '_get_bdm_image_metadata')
@mock.patch('nova.utils.get_bdm_image_metadata')
def test_create_instance_with_bdms_and_empty_imageRef(
self, mock_bdm_image_metadata, mock_validate_bdm, mock_get_volumes):
mock_bdm_image_metadata.return_value = {}
@ -4971,7 +4971,7 @@ class ServersControllerCreateTest(test.TestCase):
self.assertRaises(webob.exc.HTTPBadRequest,
self._test_create, params, no_image=True)
@mock.patch('nova.compute.api.API._get_bdm_image_metadata')
@mock.patch('nova.utils.get_bdm_image_metadata')
def test_create_instance_non_bootable_volume_fails(self, fake_bdm_meta):
params = {'block_device_mapping_v2': self.bdm_v2}
fake_bdm_meta.side_effect = exception.InvalidBDMVolumeNotBootable(id=1)
@ -5042,7 +5042,7 @@ class ServersControllerCreateTest(test.TestCase):
self.stub_out('nova.compute.api.API.create', create)
self._test_create_bdm(params)
@mock.patch.object(compute_api.API, '_get_bdm_image_metadata')
@mock.patch('nova.utils.get_bdm_image_metadata')
def test_create_instance_with_volumes_enabled_and_bdms_no_image(
self, mock_get_bdm_image_metadata):
"""Test that the create works if there is no image supplied but
@ -5066,9 +5066,9 @@ class ServersControllerCreateTest(test.TestCase):
self.stub_out('nova.compute.api.API.create', create)
self._test_create_bdm(params, no_image=True)
mock_get_bdm_image_metadata.assert_called_once_with(
mock.ANY, self.bdm, True)
mock.ANY, mock.ANY, mock.ANY, self.bdm, True)
@mock.patch.object(compute_api.API, '_get_bdm_image_metadata')
@mock.patch('nova.utils.get_bdm_image_metadata')
def test_create_instance_with_imageRef_as_empty_string(
self, mock_bdm_image_metadata):
volume = {
@ -5111,7 +5111,7 @@ class ServersControllerCreateTest(test.TestCase):
self.assertRaises(exception.ValidationError,
self._test_create_bdm, params)
@mock.patch('nova.compute.api.API._get_bdm_image_metadata')
@mock.patch('nova.utils.get_bdm_image_metadata')
def test_create_instance_non_bootable_volume_fails_legacy_bdm(
self, fake_bdm_meta):
bdm = [{

View File

@ -681,8 +681,9 @@ class ComputeVolumeTestCase(BaseTestCase):
'delete_on_termination': False,
}]
image_meta = self.compute_api._get_bdm_image_metadata(
self.context, block_device_mapping)
image_meta = utils.get_bdm_image_metadata(
self.context, self.compute_api.image_api,
self.compute_api.volume_api, block_device_mapping)
if metadata:
self.assertEqual(image_meta['properties']['vol_test_key'],
'vol_test_value')
@ -701,8 +702,10 @@ class ComputeVolumeTestCase(BaseTestCase):
'delete_on_termination': False,
}]
image_meta = self.compute_api._get_bdm_image_metadata(
self.context, block_device_mapping, legacy_bdm=False)
image_meta = utils.get_bdm_image_metadata(
self.context, self.compute_api.image_api,
self.compute_api.volume_api, block_device_mapping,
legacy_bdm=False)
if metadata:
self.assertEqual(image_meta['properties']['vol_test_key'],
'vol_test_value')
@ -734,8 +737,10 @@ class ComputeVolumeTestCase(BaseTestCase):
'delete_on_termination': True,
}]
image_meta = self.compute_api._get_bdm_image_metadata(
self.context, block_device_mapping, legacy_bdm=False)
image_meta = utils.get_bdm_image_metadata(
self.context, self.compute_api.image_api,
self.compute_api.volume_api, block_device_mapping,
legacy_bdm=False)
if metadata:
self.assertEqual('img_test_value',

View File

@ -3516,11 +3516,14 @@ class _ComputeAPIUnitTestMixIn(object):
side_effect=get_vol_data):
if not is_bootable:
self.assertRaises(exception.InvalidBDMVolumeNotBootable,
self.compute_api._get_bdm_image_metadata,
self.context, block_device_mapping)
utils.get_bdm_image_metadata,
self.context, self.compute_api.image_api,
self.compute_api.volume_api,
block_device_mapping)
else:
meta = self.compute_api._get_bdm_image_metadata(self.context,
block_device_mapping)
meta = utils.get_bdm_image_metadata(
self.context, self.compute_api.image_api,
self.compute_api.volume_api, block_device_mapping)
self.assertEqual(expected_meta, meta)
def test_boot_volume_non_bootable(self):
@ -3543,8 +3546,9 @@ class _ComputeAPIUnitTestMixIn(object):
{"min_ram": 256, "min_disk": 128, "foo": "bar"}}
with mock.patch.object(self.compute_api.volume_api, 'get',
return_value=fake_volume):
meta = self.compute_api._get_bdm_image_metadata(
self.context, block_device_mapping)
meta = utils.get_bdm_image_metadata(
self.context, self.compute_api.image_api,
self.compute_api.volume_api, block_device_mapping)
self.assertEqual(256, meta['min_ram'])
self.assertEqual(128, meta['min_disk'])
self.assertEqual('active', meta['status'])
@ -3569,8 +3573,9 @@ class _ComputeAPIUnitTestMixIn(object):
mock.patch.object(self.compute_api.volume_api, 'get_snapshot',
return_value=fake_snapshot)) as (
volume_get, volume_get_snapshot):
meta = self.compute_api._get_bdm_image_metadata(
self.context, block_device_mapping)
meta = utils.get_bdm_image_metadata(
self.context, self.compute_api.image_api,
self.compute_api.volume_api, block_device_mapping)
self.assertEqual(256, meta['min_ram'])
self.assertEqual(128, meta['min_disk'])
self.assertEqual('active', meta['status'])
@ -4443,8 +4448,10 @@ class _ComputeAPIUnitTestMixIn(object):
'device_name': 'vda',
}))]
self.assertRaises(exception.CinderConnectionFailed,
self.compute_api._get_bdm_image_metadata,
utils.get_bdm_image_metadata,
self.context,
self.compute_api.image_api,
self.compute_api.volume_api,
bdms, legacy_bdm=True)
def test_get_volumes_for_bdms_errors(self):

View File

@ -50,6 +50,7 @@ from oslo_utils import units
import six
from six.moves import range
from nova import block_device
import nova.conf
from nova import exception
from nova.i18n import _, _LE, _LW
@ -782,6 +783,61 @@ def get_image_from_system_metadata(system_meta):
return image_meta
def get_bdm_image_metadata(context, image_api, volume_api,
block_device_mapping, legacy_bdm=True):
"""Attempt to retrive image metadata from a given block_device_mapping.
If we are booting from a volume, we need to get the volume details from
Cinder and make sure we pass the metadata back accordingly.
:param context: request context
:param image_api: Image API
:param volume_api: Volume API
:param block_device_mapping:
:param legacy_bdm:
"""
if not block_device_mapping:
return {}
for bdm in block_device_mapping:
if (legacy_bdm and
block_device.get_device_letter(
bdm.get('device_name', '')) != 'a'):
continue
elif not legacy_bdm and bdm.get('boot_index') != 0:
continue
volume_id = bdm.get('volume_id')
snapshot_id = bdm.get('snapshot_id')
if snapshot_id:
# NOTE(alaski): A volume snapshot inherits metadata from the
# originating volume, but the API does not expose metadata
# on the snapshot itself. So we query the volume for it below.
snapshot = volume_api.get_snapshot(context, snapshot_id)
volume_id = snapshot['volume_id']
if bdm.get('image_id'):
try:
image_id = bdm['image_id']
image_meta = image_api.get(context, image_id)
return image_meta
except Exception:
raise exception.InvalidBDMImage(id=image_id)
elif volume_id:
try:
volume = volume_api.get(context, volume_id)
except exception.CinderConnectionFailed:
raise
except Exception:
raise exception.InvalidBDMVolume(id=volume_id)
if not volume.get('bootable', True):
raise exception.InvalidBDMVolumeNotBootable(id=volume_id)
return get_image_metadata_from_volume(volume)
return {}
def get_image_metadata_from_volume(volume):
properties = copy.copy(volume.get('volume_image_metadata', {}))
image_meta = {'properties': properties}