Merge "compute: Extract _get_bdm_image_metadata into nova.utils"
This commit is contained in:
commit
065fc9e981
|
@ -1382,53 +1382,6 @@ class API(base.Base):
|
||||||
|
|
||||||
return certs_to_return
|
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
|
@staticmethod
|
||||||
def _get_requested_instance_group(context, filter_properties):
|
def _get_requested_instance_group(context, filter_properties):
|
||||||
if (not filter_properties or
|
if (not filter_properties or
|
||||||
|
@ -1481,8 +1434,9 @@ class API(base.Base):
|
||||||
"when booting from volume")
|
"when booting from volume")
|
||||||
raise exception.CertificateValidationFailed(message=msg)
|
raise exception.CertificateValidationFailed(message=msg)
|
||||||
image_id = None
|
image_id = None
|
||||||
boot_meta = self._get_bdm_image_metadata(
|
boot_meta = utils.get_bdm_image_metadata(
|
||||||
context, block_device_mapping, legacy_bdm)
|
context, self.image_api, self.volume_api, block_device_mapping,
|
||||||
|
legacy_bdm)
|
||||||
|
|
||||||
self._check_auto_disk_config(image=boot_meta,
|
self._check_auto_disk_config(image=boot_meta,
|
||||||
auto_disk_config=auto_disk_config)
|
auto_disk_config=auto_disk_config)
|
||||||
|
|
|
@ -4685,7 +4685,7 @@ class ServersControllerCreateTest(test.TestCase):
|
||||||
|
|
||||||
@mock.patch('nova.compute.api.API._get_volumes_for_bdms')
|
@mock.patch('nova.compute.api.API._get_volumes_for_bdms')
|
||||||
@mock.patch.object(compute_api.API, '_validate_bdm')
|
@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(
|
def test_create_instance_with_bdms_and_no_image(
|
||||||
self, mock_bdm_image_metadata, mock_validate_bdm, mock_get_vols):
|
self, mock_bdm_image_metadata, mock_validate_bdm, mock_get_vols):
|
||||||
mock_bdm_image_metadata.return_value = {}
|
mock_bdm_image_metadata.return_value = {}
|
||||||
|
@ -4706,11 +4706,11 @@ class ServersControllerCreateTest(test.TestCase):
|
||||||
|
|
||||||
mock_validate_bdm.assert_called_once()
|
mock_validate_bdm.assert_called_once()
|
||||||
mock_bdm_image_metadata.assert_called_once_with(
|
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('nova.compute.api.API._get_volumes_for_bdms')
|
||||||
@mock.patch.object(compute_api.API, '_validate_bdm')
|
@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(
|
def test_create_instance_with_bdms_and_empty_imageRef(
|
||||||
self, mock_bdm_image_metadata, mock_validate_bdm, mock_get_volumes):
|
self, mock_bdm_image_metadata, mock_validate_bdm, mock_get_volumes):
|
||||||
mock_bdm_image_metadata.return_value = {}
|
mock_bdm_image_metadata.return_value = {}
|
||||||
|
@ -4971,7 +4971,7 @@ class ServersControllerCreateTest(test.TestCase):
|
||||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self._test_create, params, no_image=True)
|
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):
|
def test_create_instance_non_bootable_volume_fails(self, fake_bdm_meta):
|
||||||
params = {'block_device_mapping_v2': self.bdm_v2}
|
params = {'block_device_mapping_v2': self.bdm_v2}
|
||||||
fake_bdm_meta.side_effect = exception.InvalidBDMVolumeNotBootable(id=1)
|
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.stub_out('nova.compute.api.API.create', create)
|
||||||
self._test_create_bdm(params)
|
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(
|
def test_create_instance_with_volumes_enabled_and_bdms_no_image(
|
||||||
self, mock_get_bdm_image_metadata):
|
self, mock_get_bdm_image_metadata):
|
||||||
"""Test that the create works if there is no image supplied but
|
"""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.stub_out('nova.compute.api.API.create', create)
|
||||||
self._test_create_bdm(params, no_image=True)
|
self._test_create_bdm(params, no_image=True)
|
||||||
mock_get_bdm_image_metadata.assert_called_once_with(
|
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(
|
def test_create_instance_with_imageRef_as_empty_string(
|
||||||
self, mock_bdm_image_metadata):
|
self, mock_bdm_image_metadata):
|
||||||
volume = {
|
volume = {
|
||||||
|
@ -5111,7 +5111,7 @@ class ServersControllerCreateTest(test.TestCase):
|
||||||
self.assertRaises(exception.ValidationError,
|
self.assertRaises(exception.ValidationError,
|
||||||
self._test_create_bdm, params)
|
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(
|
def test_create_instance_non_bootable_volume_fails_legacy_bdm(
|
||||||
self, fake_bdm_meta):
|
self, fake_bdm_meta):
|
||||||
bdm = [{
|
bdm = [{
|
||||||
|
|
|
@ -681,8 +681,9 @@ class ComputeVolumeTestCase(BaseTestCase):
|
||||||
'delete_on_termination': False,
|
'delete_on_termination': False,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
image_meta = self.compute_api._get_bdm_image_metadata(
|
image_meta = utils.get_bdm_image_metadata(
|
||||||
self.context, block_device_mapping)
|
self.context, self.compute_api.image_api,
|
||||||
|
self.compute_api.volume_api, block_device_mapping)
|
||||||
if metadata:
|
if metadata:
|
||||||
self.assertEqual(image_meta['properties']['vol_test_key'],
|
self.assertEqual(image_meta['properties']['vol_test_key'],
|
||||||
'vol_test_value')
|
'vol_test_value')
|
||||||
|
@ -701,8 +702,10 @@ class ComputeVolumeTestCase(BaseTestCase):
|
||||||
'delete_on_termination': False,
|
'delete_on_termination': False,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
image_meta = self.compute_api._get_bdm_image_metadata(
|
image_meta = utils.get_bdm_image_metadata(
|
||||||
self.context, block_device_mapping, legacy_bdm=False)
|
self.context, self.compute_api.image_api,
|
||||||
|
self.compute_api.volume_api, block_device_mapping,
|
||||||
|
legacy_bdm=False)
|
||||||
if metadata:
|
if metadata:
|
||||||
self.assertEqual(image_meta['properties']['vol_test_key'],
|
self.assertEqual(image_meta['properties']['vol_test_key'],
|
||||||
'vol_test_value')
|
'vol_test_value')
|
||||||
|
@ -734,8 +737,10 @@ class ComputeVolumeTestCase(BaseTestCase):
|
||||||
'delete_on_termination': True,
|
'delete_on_termination': True,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
image_meta = self.compute_api._get_bdm_image_metadata(
|
image_meta = utils.get_bdm_image_metadata(
|
||||||
self.context, block_device_mapping, legacy_bdm=False)
|
self.context, self.compute_api.image_api,
|
||||||
|
self.compute_api.volume_api, block_device_mapping,
|
||||||
|
legacy_bdm=False)
|
||||||
|
|
||||||
if metadata:
|
if metadata:
|
||||||
self.assertEqual('img_test_value',
|
self.assertEqual('img_test_value',
|
||||||
|
|
|
@ -3516,11 +3516,14 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||||
side_effect=get_vol_data):
|
side_effect=get_vol_data):
|
||||||
if not is_bootable:
|
if not is_bootable:
|
||||||
self.assertRaises(exception.InvalidBDMVolumeNotBootable,
|
self.assertRaises(exception.InvalidBDMVolumeNotBootable,
|
||||||
self.compute_api._get_bdm_image_metadata,
|
utils.get_bdm_image_metadata,
|
||||||
self.context, block_device_mapping)
|
self.context, self.compute_api.image_api,
|
||||||
|
self.compute_api.volume_api,
|
||||||
|
block_device_mapping)
|
||||||
else:
|
else:
|
||||||
meta = self.compute_api._get_bdm_image_metadata(self.context,
|
meta = utils.get_bdm_image_metadata(
|
||||||
block_device_mapping)
|
self.context, self.compute_api.image_api,
|
||||||
|
self.compute_api.volume_api, block_device_mapping)
|
||||||
self.assertEqual(expected_meta, meta)
|
self.assertEqual(expected_meta, meta)
|
||||||
|
|
||||||
def test_boot_volume_non_bootable(self):
|
def test_boot_volume_non_bootable(self):
|
||||||
|
@ -3543,8 +3546,9 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||||
{"min_ram": 256, "min_disk": 128, "foo": "bar"}}
|
{"min_ram": 256, "min_disk": 128, "foo": "bar"}}
|
||||||
with mock.patch.object(self.compute_api.volume_api, 'get',
|
with mock.patch.object(self.compute_api.volume_api, 'get',
|
||||||
return_value=fake_volume):
|
return_value=fake_volume):
|
||||||
meta = self.compute_api._get_bdm_image_metadata(
|
meta = utils.get_bdm_image_metadata(
|
||||||
self.context, block_device_mapping)
|
self.context, self.compute_api.image_api,
|
||||||
|
self.compute_api.volume_api, block_device_mapping)
|
||||||
self.assertEqual(256, meta['min_ram'])
|
self.assertEqual(256, meta['min_ram'])
|
||||||
self.assertEqual(128, meta['min_disk'])
|
self.assertEqual(128, meta['min_disk'])
|
||||||
self.assertEqual('active', meta['status'])
|
self.assertEqual('active', meta['status'])
|
||||||
|
@ -3569,8 +3573,9 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||||
mock.patch.object(self.compute_api.volume_api, 'get_snapshot',
|
mock.patch.object(self.compute_api.volume_api, 'get_snapshot',
|
||||||
return_value=fake_snapshot)) as (
|
return_value=fake_snapshot)) as (
|
||||||
volume_get, volume_get_snapshot):
|
volume_get, volume_get_snapshot):
|
||||||
meta = self.compute_api._get_bdm_image_metadata(
|
meta = utils.get_bdm_image_metadata(
|
||||||
self.context, block_device_mapping)
|
self.context, self.compute_api.image_api,
|
||||||
|
self.compute_api.volume_api, block_device_mapping)
|
||||||
self.assertEqual(256, meta['min_ram'])
|
self.assertEqual(256, meta['min_ram'])
|
||||||
self.assertEqual(128, meta['min_disk'])
|
self.assertEqual(128, meta['min_disk'])
|
||||||
self.assertEqual('active', meta['status'])
|
self.assertEqual('active', meta['status'])
|
||||||
|
@ -4443,8 +4448,10 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||||
'device_name': 'vda',
|
'device_name': 'vda',
|
||||||
}))]
|
}))]
|
||||||
self.assertRaises(exception.CinderConnectionFailed,
|
self.assertRaises(exception.CinderConnectionFailed,
|
||||||
self.compute_api._get_bdm_image_metadata,
|
utils.get_bdm_image_metadata,
|
||||||
self.context,
|
self.context,
|
||||||
|
self.compute_api.image_api,
|
||||||
|
self.compute_api.volume_api,
|
||||||
bdms, legacy_bdm=True)
|
bdms, legacy_bdm=True)
|
||||||
|
|
||||||
def test_get_volumes_for_bdms_errors(self):
|
def test_get_volumes_for_bdms_errors(self):
|
||||||
|
|
|
@ -50,6 +50,7 @@ from oslo_utils import units
|
||||||
import six
|
import six
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
|
|
||||||
|
from nova import block_device
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _, _LE, _LW
|
from nova.i18n import _, _LE, _LW
|
||||||
|
@ -782,6 +783,61 @@ def get_image_from_system_metadata(system_meta):
|
||||||
return image_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):
|
def get_image_metadata_from_volume(volume):
|
||||||
properties = copy.copy(volume.get('volume_image_metadata', {}))
|
properties = copy.copy(volume.get('volume_image_metadata', {}))
|
||||||
image_meta = {'properties': properties}
|
image_meta = {'properties': properties}
|
||||||
|
|
Loading…
Reference in New Issue