utils: Move 'get_bdm_image_metadata' to nova.block_device

The 'nova.block_device' module is essentially a catchall utils-like
module for all things BDM. The 'get_bdm_image_metadata' module, and
closely related 'get_image_metadata_from_volume' both fall into the
category of functions that belong here so move them. This allows us to
clean up tests and, crucially, avoid a circular reference seen when we
want to use proper type hints in the 'nova.virt.driver' module.

  nova.context imports...
  nova.utils, which imports...
  nova.block_device, which imports...
  nova.virt.driver, which tries to import...
  nova.context, causing a circular dependency

Change-Id: I48177d6e93f2ff132d26b53cd682fd24a43a4b31
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane
2020-07-08 11:42:47 +01:00
parent bc784a1c1f
commit 72cf37bca0
10 changed files with 283 additions and 251 deletions

View File

@@ -13,11 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
import re
from oslo_log import log as logging
from oslo_utils import strutils
from oslo_utils import units
import nova.conf
from nova import exception
@@ -34,6 +35,12 @@ _DEFAULT_MAPPINGS = {'ami': 'sda1',
'root': DEFAULT_ROOT_DEV_NAME,
'swap': 'sda3'}
# Image attributes which Cinder stores in volume image metadata
# as regular properties
VIM_IMAGE_ATTRIBUTES = (
'image_id', 'image_name', 'size', 'checksum',
'container_format', 'disk_format', 'min_ram', 'min_disk',
)
bdm_legacy_fields = set(['device_name', 'delete_on_termination',
'virtual_name', 'snapshot_id',
@@ -629,3 +636,86 @@ def get_bdm_swap_list(block_device_mappings):
def get_bdm_local_disk_num(block_device_mappings):
return len([bdm for bdm in block_device_mappings
if bdm.get('destination_type') == 'local'])
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
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}
# Volume size is no longer related to the original image size,
# so we take it from the volume directly. Cinder creates
# volumes in Gb increments, and stores size in Gb, whereas
# glance reports size in bytes. As we're returning glance
# metadata here, we need to convert it.
image_meta['size'] = volume.get('size', 0) * units.Gi
# NOTE(yjiang5): restore the basic attributes
# NOTE(mdbooth): These values come from volume_glance_metadata
# in cinder. This is a simple key/value table, and all values
# are strings. We need to convert them to ints to avoid
# unexpected type errors.
for attr in VIM_IMAGE_ATTRIBUTES:
val = properties.pop(attr, None)
if attr in ('min_ram', 'min_disk'):
image_meta[attr] = int(val or 0)
# NOTE(mriedem): Set the status to 'active' as a really old hack
# from when this method was in the compute API class and is
# needed for _validate_flavor_image which makes sure the image
# is 'active'. For volume-backed servers, if the volume is not
# available because the image backing the volume is not active,
# then the compute API trying to reserve the volume should fail.
image_meta['status'] = 'active'
return image_meta