mypy: create_volume flows

Change-Id: I1641ccbbaaac4ee0745071c847f0829235616e04
This commit is contained in:
Eric Harney 2021-03-15 11:19:23 -04:00
parent d2dd034cf4
commit b10f71e429
9 changed files with 227 additions and 80 deletions

View File

@ -11,6 +11,7 @@
# under the License. # under the License.
import os import os
from typing import Any, List # noqa: H301
from oslo_log import log as logging from oslo_log import log as logging
# For more information please visit: https://wiki.openstack.org/wiki/TaskFlow # For more information please visit: https://wiki.openstack.org/wiki/TaskFlow
@ -24,7 +25,7 @@ from cinder import exception
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def _make_task_name(cls, addons=None): def _make_task_name(cls, addons: List[str] = None) -> str:
"""Makes a pretty name for a task class.""" """Makes a pretty name for a task class."""
base_name = ".".join([cls.__module__, cls.__name__]) base_name = ".".join([cls.__module__, cls.__name__])
extra = '' extra = ''
@ -40,11 +41,11 @@ class CinderTask(task.Task):
implement the given task as the task name. implement the given task as the task name.
""" """
def __init__(self, addons=None, **kwargs): def __init__(self, addons: List[str] = None, **kwargs: Any) -> None:
super(CinderTask, self).__init__(self.make_name(addons), **kwargs) super(CinderTask, self).__init__(self.make_name(addons), **kwargs)
@classmethod @classmethod
def make_name(cls, addons=None): def make_name(cls, addons: List[str] = None) -> str:
return _make_task_name(cls, addons) return _make_task_name(cls, addons)

View File

@ -133,7 +133,8 @@ def as_int(obj: Union[int, float, str], quiet: bool = True) -> int:
return obj return obj
def check_exclusive_options(**kwargs: dict) -> None: def check_exclusive_options(
**kwargs: Optional[Union[dict, str, bool]]) -> None:
"""Checks that only one of the provided options is actually not-none. """Checks that only one of the provided options is actually not-none.
Iterates over all the kwargs passed in and checks that only one of said Iterates over all the kwargs passed in and checks that only one of said

View File

@ -10,15 +10,19 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from typing import Any, Dict, List, Optional, Tuple, Type, Union # noqa: H301
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
import taskflow.engines import taskflow.engines
from taskflow.patterns import linear_flow from taskflow.patterns import linear_flow
from taskflow.types import failure as ft from taskflow.types import failure as ft
from cinder import context
from cinder import exception from cinder import exception
from cinder import flow_utils from cinder import flow_utils
from cinder.i18n import _ from cinder.i18n import _
from cinder.image import glance
from cinder import objects from cinder import objects
from cinder.objects import fields from cinder.objects import fields
from cinder.policies import volumes as policy from cinder.policies import volumes as policy
@ -68,15 +72,20 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
'refresh_az', 'backup_id', 'availability_zones', 'refresh_az', 'backup_id', 'availability_zones',
'multiattach']) 'multiattach'])
def __init__(self, image_service, availability_zones, **kwargs): def __init__(self,
image_service: glance.GlanceImageService,
availability_zones, **kwargs) -> None:
super(ExtractVolumeRequestTask, self).__init__(addons=[ACTION], super(ExtractVolumeRequestTask, self).__init__(addons=[ACTION],
**kwargs) **kwargs)
self.image_service = image_service self.image_service = image_service
self.availability_zones = availability_zones self.availability_zones = availability_zones
@staticmethod @staticmethod
def _extract_resource(resource, allowed_vals, exc, resource_name, def _extract_resource(resource: Optional[dict],
props=('status',)): allowed_vals: Tuple[Tuple[str, ...]],
exc: Type[exception.CinderException],
resource_name: str,
props: Tuple[str] = ('status',)) -> Optional[str]:
"""Extracts the resource id from the provided resource. """Extracts the resource id from the provided resource.
This method validates the input resource dict and checks that the This method validates the input resource dict and checks that the
@ -109,36 +118,51 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
resource_id = resource['id'] resource_id = resource['id']
return resource_id return resource_id
def _extract_consistencygroup(self, consistencygroup): def _extract_consistencygroup(
self,
consistencygroup: Optional[dict]) -> Optional[str]:
return self._extract_resource(consistencygroup, (CG_PROCEED_STATUS,), return self._extract_resource(consistencygroup, (CG_PROCEED_STATUS,),
exception.InvalidConsistencyGroup, exception.InvalidConsistencyGroup,
'consistencygroup') 'consistencygroup')
def _extract_group(self, group): def _extract_group(
self,
group: Optional[dict]) -> Optional[str]:
return self._extract_resource(group, (GROUP_PROCEED_STATUS,), return self._extract_resource(group, (GROUP_PROCEED_STATUS,),
exception.InvalidGroup, exception.InvalidGroup,
'group') 'group')
def _extract_cgsnapshot(self, cgsnapshot): def _extract_cgsnapshot(
self,
cgsnapshot: Optional[dict]) -> Optional[str]:
return self._extract_resource(cgsnapshot, (CGSNAPSHOT_PROCEED_STATUS,), return self._extract_resource(cgsnapshot, (CGSNAPSHOT_PROCEED_STATUS,),
exception.InvalidCgSnapshot, exception.InvalidCgSnapshot,
'CGSNAPSHOT') 'CGSNAPSHOT')
def _extract_snapshot(self, snapshot): def _extract_snapshot(
self,
snapshot: Optional[dict]) -> Optional[str]:
return self._extract_resource(snapshot, (SNAPSHOT_PROCEED_STATUS,), return self._extract_resource(snapshot, (SNAPSHOT_PROCEED_STATUS,),
exception.InvalidSnapshot, 'snapshot') exception.InvalidSnapshot, 'snapshot')
def _extract_source_volume(self, source_volume): def _extract_source_volume(
self,
source_volume: Optional[dict]) -> Optional[str]:
return self._extract_resource(source_volume, (SRC_VOL_PROCEED_STATUS,), return self._extract_resource(source_volume, (SRC_VOL_PROCEED_STATUS,),
exception.InvalidVolume, 'source volume') exception.InvalidVolume, 'source volume')
def _extract_backup(self, backup): def _extract_backup(
self,
backup: Optional[dict]) -> Optional[str]:
return self._extract_resource(backup, (BACKUP_PROCEED_STATUS,), return self._extract_resource(backup, (BACKUP_PROCEED_STATUS,),
exception.InvalidBackup, exception.InvalidBackup,
'backup') 'backup')
@staticmethod @staticmethod
def _extract_size(size, source_volume, snapshot, backup): def _extract_size(size: int,
source_volume: Optional[objects.Volume],
snapshot: Optional[objects.Snapshot],
backup: Optional[objects.Backup]) -> int:
"""Extracts and validates the volume size. """Extracts and validates the volume size.
This function will validate or when not provided fill in the provided This function will validate or when not provided fill in the provided
@ -146,7 +170,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
validation on the size that is found and returns said validated size. validation on the size that is found and returns said validated size.
""" """
def validate_snap_size(size): def validate_snap_size(size: int) -> None:
if snapshot and size < snapshot.volume_size: if snapshot and size < snapshot.volume_size:
msg = _("Volume size '%(size)s'GB cannot be smaller than" msg = _("Volume size '%(size)s'GB cannot be smaller than"
" the snapshot size %(snap_size)sGB. " " the snapshot size %(snap_size)sGB. "
@ -155,7 +179,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
'snap_size': snapshot.volume_size} 'snap_size': snapshot.volume_size}
raise exception.InvalidInput(reason=msg) raise exception.InvalidInput(reason=msg)
def validate_source_size(size): def validate_source_size(size: int) -> None:
if source_volume and size < source_volume['size']: if source_volume and size < source_volume['size']:
msg = _("Volume size '%(size)s'GB cannot be smaller than " msg = _("Volume size '%(size)s'GB cannot be smaller than "
"original volume size %(source_size)sGB. " "original volume size %(source_size)sGB. "
@ -164,7 +188,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
'source_size': source_volume['size']} 'source_size': source_volume['size']}
raise exception.InvalidInput(reason=msg) raise exception.InvalidInput(reason=msg)
def validate_backup_size(size): def validate_backup_size(size: int) -> None:
if backup and size < backup['size']: if backup and size < backup['size']:
msg = _("Volume size %(size)sGB cannot be smaller than " msg = _("Volume size %(size)sGB cannot be smaller than "
"the backup size %(backup_size)sGB. " "the backup size %(backup_size)sGB. "
@ -173,7 +197,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
'backup_size': backup['size']} 'backup_size': backup['size']}
raise exception.InvalidInput(reason=msg) raise exception.InvalidInput(reason=msg)
def validate_int(size): def validate_int(size: int) -> None:
if not isinstance(size, int) or size <= 0: if not isinstance(size, int) or size <= 0:
msg = _("Volume size '%(size)s' must be an integer and" msg = _("Volume size '%(size)s' must be an integer and"
" greater than 0") % {'size': size} " greater than 0") % {'size': size}
@ -206,7 +230,10 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
func(size) func(size)
return size return size
def _get_image_metadata(self, context, image_id, size): def _get_image_metadata(self,
context: context.RequestContext,
image_id: Optional[str],
size: int) -> Optional[Dict[str, Any]]:
"""Checks image existence and validates the image metadata. """Checks image existence and validates the image metadata.
Returns: image metadata or None Returns: image metadata or None
@ -224,8 +251,13 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
return image_meta return image_meta
def _extract_availability_zones(self, availability_zone, snapshot, def _extract_availability_zones(
source_volume, group, volume_type=None): self,
availability_zone: Optional[str],
snapshot,
source_volume,
group: Optional[dict],
volume_type: Dict[str, Any] = None) -> Tuple[List[str], bool]:
"""Extracts and returns a validated availability zone list. """Extracts and returns a validated availability zone list.
This function will extract the availability zone (if not provided) from This function will extract the availability zone (if not provided) from
@ -238,6 +270,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
volume_type) volume_type)
type_az_configured = type_azs is not None type_az_configured = type_azs is not None
if type_az_configured: if type_az_configured:
assert type_azs is not None
safe_azs = list( safe_azs = list(
set(type_azs).intersection(self.availability_zones)) set(type_azs).intersection(self.availability_zones))
if not safe_azs: if not safe_azs:
@ -317,11 +350,17 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
else: else:
return safe_azs, refresh_az return safe_azs, refresh_az
def _get_encryption_key_id(self, key_manager, context, volume_type_id, def _get_encryption_key_id(
snapshot, source_volume, self,
image_metadata): key_manager,
encryption_key_id = None context: context.RequestContext,
volume_type_id: str,
snapshot: Optional[objects.Snapshot],
source_volume: Optional[objects.Volume],
image_metadata: Optional[Dict[str, Any]]) -> Optional[str]:
if volume_types.is_encrypted(context, volume_type_id): if volume_types.is_encrypted(context, volume_type_id):
encryption_key_id = None
if snapshot is not None: # creating from snapshot if snapshot is not None: # creating from snapshot
encryption_key_id = snapshot['encryption_key_id'] encryption_key_id = snapshot['encryption_key_id']
elif source_volume is not None: # cloning volume elif source_volume is not None: # cloning volume
@ -335,22 +374,29 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
# be copied because the key is deleted when the volume is deleted. # be copied because the key is deleted when the volume is deleted.
# Clone the existing key and associate a separate -- but # Clone the existing key and associate a separate -- but
# identical -- key with each volume. # identical -- key with each volume.
new_encryption_key_id: Optional[str]
if encryption_key_id is not None: if encryption_key_id is not None:
encryption_key_id = volume_utils.clone_encryption_key( new_encryption_key_id = volume_utils.clone_encryption_key(
context, context,
key_manager, key_manager,
encryption_key_id) encryption_key_id)
else: else:
encryption_key_id = volume_utils.create_encryption_key( new_encryption_key_id = volume_utils.create_encryption_key(
context, context,
key_manager, key_manager,
volume_type_id) volume_type_id)
return encryption_key_id return new_encryption_key_id
else:
return None
@staticmethod @staticmethod
def _get_volume_type(context, volume_type, def _get_volume_type(
source_volume, snapshot, image_volume_type_id): context: context.RequestContext,
volume_type: Optional[Any],
source_volume: Optional[objects.Volume],
snapshot: Optional[objects.Snapshot],
image_volume_type_id: Optional[str]) -> objects.VolumeType:
"""Returns a volume_type object or raises. Never returns None.""" """Returns a volume_type object or raises. Never returns None."""
if volume_type: if volume_type:
return volume_type return volume_type
@ -380,10 +426,22 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
# otherwise, use the default volume type # otherwise, use the default volume type
return volume_types.get_default_volume_type(context) return volume_types.get_default_volume_type(context)
def execute(self, context, size, snapshot, image_id, source_volume, def execute(self,
availability_zone, volume_type, metadata, key_manager, context: context.RequestContext,
consistencygroup, cgsnapshot, group, group_snapshot, backup, size: int,
multiattach=False): snapshot: Optional[dict],
image_id: Optional[str],
source_volume: Optional[dict],
availability_zone: Optional[str],
volume_type,
metadata,
key_manager,
consistencygroup,
cgsnapshot,
group,
group_snapshot,
backup: Optional[dict],
multiattach: bool = False) -> Dict[str, Any]:
utils.check_exclusive_options(snapshot=snapshot, utils.check_exclusive_options(snapshot=snapshot,
imageRef=image_id, imageRef=image_id,
@ -443,7 +501,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
if multiattach: if multiattach:
context.authorize(policy.MULTIATTACH_POLICY) context.authorize(policy.MULTIATTACH_POLICY)
specs = {} specs: Optional[Dict] = {}
if volume_type_id: if volume_type_id:
qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id) qos_specs = volume_types.get_volume_type_qos_specs(volume_type_id)
if qos_specs['qos_specs']: if qos_specs['qos_specs']:
@ -489,7 +547,7 @@ class EntryCreateTask(flow_utils.CinderTask):
default_provides = set(['volume_properties', 'volume_id', 'volume']) default_provides = set(['volume_properties', 'volume_id', 'volume'])
def __init__(self): def __init__(self) -> None:
requires = ['description', 'metadata', requires = ['description', 'metadata',
'name', 'reservations', 'size', 'snapshot_id', 'name', 'reservations', 'size', 'snapshot_id',
'source_volid', 'volume_type_id', 'encryption_key_id', 'source_volid', 'volume_type_id', 'encryption_key_id',
@ -498,7 +556,10 @@ class EntryCreateTask(flow_utils.CinderTask):
super(EntryCreateTask, self).__init__(addons=[ACTION], super(EntryCreateTask, self).__init__(addons=[ACTION],
requires=requires) requires=requires)
def execute(self, context, optional_args, **kwargs): def execute(self,
context: context.RequestContext,
optional_args: dict,
**kwargs) -> Dict[str, Any]:
"""Creates a database entry for the given inputs and returns details. """Creates a database entry for the given inputs and returns details.
Accesses the database and creates a new entry for the to be created Accesses the database and creates a new entry for the to be created
@ -569,7 +630,11 @@ class EntryCreateTask(flow_utils.CinderTask):
'volume': volume, 'volume': volume,
} }
def revert(self, context, result, optional_args, **kwargs): def revert(self,
context: context.RequestContext,
result: Union[dict, ft.Failure],
optional_args: dict,
**kwargs) -> None:
if isinstance(result, ft.Failure): if isinstance(result, ft.Failure):
# We never produced a result and therefore can't destroy anything. # We never produced a result and therefore can't destroy anything.
return return
@ -610,8 +675,12 @@ class QuotaReserveTask(flow_utils.CinderTask):
def __init__(self): def __init__(self):
super(QuotaReserveTask, self).__init__(addons=[ACTION]) super(QuotaReserveTask, self).__init__(addons=[ACTION])
def execute(self, context, size, volume_type_id, group_snapshot, def execute(self,
optional_args): context: context.RequestContext,
size: int,
volume_type_id,
group_snapshot: Optional[objects.Snapshot],
optional_args: dict) -> Optional[dict]:
try: try:
values = {'per_volume_gigabytes': size} values = {'per_volume_gigabytes': size}
QUOTAS.limit_check(context, project_id=context.project_id, QUOTAS.limit_check(context, project_id=context.project_id,
@ -638,8 +707,12 @@ class QuotaReserveTask(flow_utils.CinderTask):
quota_utils.process_reserve_over_quota(context, e, quota_utils.process_reserve_over_quota(context, e,
resource='volumes', resource='volumes',
size=size) size=size)
return None # TODO: is this correct?
def revert(self, context, result, optional_args, **kwargs): def revert(self,
context: context.RequestContext,
result: Union[dict, ft.Failure],
optional_args: dict, **kwargs) -> None:
# We never produced a result and therefore can't destroy anything. # We never produced a result and therefore can't destroy anything.
if isinstance(result, ft.Failure): if isinstance(result, ft.Failure):
return return
@ -678,14 +751,18 @@ class QuotaCommitTask(flow_utils.CinderTask):
def __init__(self): def __init__(self):
super(QuotaCommitTask, self).__init__(addons=[ACTION]) super(QuotaCommitTask, self).__init__(addons=[ACTION])
def execute(self, context, reservations, volume_properties, def execute(self, context: context.RequestContext,
optional_args): reservations, volume_properties,
optional_args: dict) -> dict:
QUOTAS.commit(context, reservations) QUOTAS.commit(context, reservations)
# updating is_quota_committed attribute of optional_args dictionary # updating is_quota_committed attribute of optional_args dictionary
optional_args['is_quota_committed'] = True optional_args['is_quota_committed'] = True
return {'volume_properties': volume_properties} return {'volume_properties': volume_properties}
def revert(self, context, result, **kwargs): def revert(self,
context: context.RequestContext,
result: Union[dict, ft.Failure],
**kwargs) -> None:
# We never produced a result and therefore can't destroy anything. # We never produced a result and therefore can't destroy anything.
if isinstance(result, ft.Failure): if isinstance(result, ft.Failure):
return return
@ -717,7 +794,7 @@ class VolumeCastTask(flow_utils.CinderTask):
created volume. created volume.
""" """
def __init__(self, scheduler_rpcapi, volume_rpcapi, db): def __init__(self, scheduler_rpcapi, volume_rpcapi, db) -> None:
requires = ['image_id', 'scheduler_hints', 'snapshot_id', requires = ['image_id', 'scheduler_hints', 'snapshot_id',
'source_volid', 'volume_id', 'volume', 'volume_type', 'source_volid', 'volume_id', 'volume', 'volume_type',
'volume_properties', 'consistencygroup_id', 'volume_properties', 'consistencygroup_id',
@ -729,7 +806,10 @@ class VolumeCastTask(flow_utils.CinderTask):
self.scheduler_rpcapi = scheduler_rpcapi self.scheduler_rpcapi = scheduler_rpcapi
self.db = db self.db = db
def _cast_create_volume(self, context, request_spec, filter_properties): def _cast_create_volume(self,
context: context.RequestContext,
request_spec: Dict[str, Any],
filter_properties: Dict) -> None:
source_volid = request_spec['source_volid'] source_volid = request_spec['source_volid']
volume = request_spec['volume'] volume = request_spec['volume']
snapshot_id = request_spec['snapshot_id'] snapshot_id = request_spec['snapshot_id']
@ -775,7 +855,7 @@ class VolumeCastTask(flow_utils.CinderTask):
filter_properties=filter_properties, filter_properties=filter_properties,
backup_id=backup_id) backup_id=backup_id)
def execute(self, context, **kwargs): def execute(self, context: context.RequestContext, **kwargs) -> None:
scheduler_hints = kwargs.pop('scheduler_hints', None) scheduler_hints = kwargs.pop('scheduler_hints', None)
db_vt = kwargs.pop('volume_type') db_vt = kwargs.pop('volume_type')
kwargs['volume_type'] = None kwargs['volume_type'] = None
@ -789,7 +869,12 @@ class VolumeCastTask(flow_utils.CinderTask):
filter_properties['scheduler_hints'] = scheduler_hints filter_properties['scheduler_hints'] = scheduler_hints
self._cast_create_volume(context, request_spec, filter_properties) self._cast_create_volume(context, request_spec, filter_properties)
def revert(self, context, result, flow_failures, volume, **kwargs): def revert(self,
context: context.RequestContext,
result: Union[dict, ft.Failure],
flow_failures,
volume: objects.Volume,
**kwargs) -> None:
if isinstance(result, ft.Failure): if isinstance(result, ft.Failure):
return return

View File

@ -16,6 +16,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from typing import Callable
from oslo_log import log as logging from oslo_log import log as logging
from cinder import exception from cinder import exception
@ -28,7 +30,7 @@ LOG = logging.getLogger(__name__)
REASON_LENGTH = 128 REASON_LENGTH = 128
def make_pretty_name(method): def make_pretty_name(method: Callable) -> str:
"""Makes a pretty name for a function/method.""" """Makes a pretty name for a function/method."""
meth_pieces = [method.__name__] meth_pieces = [method.__name__]
# If its an instance method attempt to tack on the class name # If its an instance method attempt to tack on the class name

View File

@ -12,6 +12,8 @@
import binascii import binascii
import traceback import traceback
import typing
from typing import Any, Dict, Optional, Tuple # noqa: H301
from castellan import key_manager from castellan import key_manager
import os_brick.initiator.connectors import os_brick.initiator.connectors
@ -381,7 +383,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
default_provides = 'volume_spec' default_provides = 'volume_spec'
def __init__(self, manager, db, driver, image_volume_cache=None): def __init__(self, manager, db, driver, image_volume_cache=None) -> None:
super(CreateVolumeFromSpecTask, self).__init__(addons=[ACTION]) super(CreateVolumeFromSpecTask, self).__init__(addons=[ACTION])
self.manager = manager self.manager = manager
self.db = db self.db = db
@ -450,8 +452,11 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
'vol_id': volume.id}) 'vol_id': volume.id})
raise exception.MetadataCopyFailure(reason=ex) raise exception.MetadataCopyFailure(reason=ex)
def _create_from_snapshot(self, context, volume, snapshot_id, def _create_from_snapshot(self,
**kwargs): context: cinder_context.RequestContext,
volume: objects.Volume,
snapshot_id: str,
**kwargs: Any) -> dict:
snapshot = objects.Snapshot.get_by_id(context, snapshot_id) snapshot = objects.Snapshot.get_by_id(context, snapshot_id)
try: try:
model_update = self.driver.create_volume_from_snapshot(volume, model_update = self.driver.create_volume_from_snapshot(volume,
@ -618,7 +623,10 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
return model_update return model_update
def _create_from_source_volume(self, context, volume, source_volid, def _create_from_source_volume(self,
context: cinder_context.RequestContext,
volume: objects.Volume,
source_volid: str,
**kwargs): **kwargs):
# NOTE(harlowja): if the source volume has disappeared this will be our # NOTE(harlowja): if the source volume has disappeared this will be our
# detection of that since this database call should fail. # detection of that since this database call should fail.
@ -645,8 +653,11 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
context, volume, source_volid=srcvol_ref.id) context, volume, source_volid=srcvol_ref.id)
return model_update return model_update
def _capture_volume_image_metadata(self, context, volume_id, def _capture_volume_image_metadata(self,
image_id, image_meta): context: cinder_context.RequestContext,
volume_id: str,
image_id: str,
image_meta: dict) -> None:
volume_metadata = volume_utils.get_volume_image_metadata( volume_metadata = volume_utils.get_volume_image_metadata(
image_id, image_meta) image_id, image_meta)
LOG.debug("Creating volume glance metadata for volume %(volume_id)s" LOG.debug("Creating volume glance metadata for volume %(volume_id)s"
@ -656,7 +667,11 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
self.db.volume_glance_metadata_bulk_create(context, volume_id, self.db.volume_glance_metadata_bulk_create(context, volume_id,
volume_metadata) volume_metadata)
def _clone_image_volume(self, context, volume, image_location, image_meta): def _clone_image_volume(self,
context: cinder_context.RequestContext,
volume: objects.Volume,
image_location,
image_meta: Dict[str, Any]) -> Tuple[None, bool]:
"""Create a volume efficiently from an existing image. """Create a volume efficiently from an existing image.
Returns a dict of volume properties eg. provider_location, Returns a dict of volume properties eg. provider_location,
@ -713,8 +728,12 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
{'id': image_volume['id']}) {'id': image_volume['id']})
return None, False return None, False
def _create_from_image_download(self, context, volume, image_location, def _create_from_image_download(self,
image_meta, image_service): context: cinder_context.RequestContext,
volume: objects.Volume,
image_location,
image_meta: Dict[str, Any],
image_service) -> dict:
# TODO(harlowja): what needs to be rolled back in the clone if this # TODO(harlowja): what needs to be rolled back in the clone if this
# volume create fails?? Likely this should be a subflow or broken # volume create fails?? Likely this should be a subflow or broken
# out task in the future. That will bring up the question of how # out task in the future. That will bring up the question of how
@ -743,14 +762,20 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
{'volume_id': volume.id}) {'volume_id': volume.id})
return model_update return model_update
def _create_from_image_cache(self, context, internal_context, volume, def _create_from_image_cache(
image_id, image_meta): self,
context: cinder_context.RequestContext,
internal_context: cinder_context.RequestContext,
volume: objects.Volume,
image_id: str,
image_meta: Dict[str, Any]) -> Tuple[None, bool]:
"""Attempt to create the volume using the image cache. """Attempt to create the volume using the image cache.
Best case this will simply clone the existing volume in the cache. Best case this will simply clone the existing volume in the cache.
Worst case the image is out of date and will be evicted. In that case Worst case the image is out of date and will be evicted. In that case
a clone will not be created and the image must be downloaded again. a clone will not be created and the image must be downloaded again.
""" """
assert self.image_volume_cache is not None
LOG.debug('Attempting to retrieve cache entry for image = ' LOG.debug('Attempting to retrieve cache entry for image = '
'%(image_id)s on host %(host)s.', '%(image_id)s on host %(host)s.',
{'image_id': image_id, 'host': volume.host}) {'image_id': image_id, 'host': volume.host})
@ -787,9 +812,15 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
return None, False return None, False
@coordination.synchronized('{image_id}') @coordination.synchronized('{image_id}')
def _prepare_image_cache_entry(self, context, volume, def _prepare_image_cache_entry(self,
image_location, image_id, context: cinder_context.RequestContext,
image_meta, image_service): volume: objects.Volume,
image_location: str,
image_id: str,
image_meta: Dict[str, Any],
image_service) -> Tuple[Optional[dict],
bool]:
assert self.image_volume_cache is not None
internal_context = cinder_context.get_internal_tenant_context() internal_context = cinder_context.get_internal_tenant_context()
if not internal_context: if not internal_context:
return None, False return None, False
@ -822,10 +853,15 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
update_cache=True) update_cache=True)
return model_update, True return model_update, True
def _create_from_image_cache_or_download(self, context, volume, def _create_from_image_cache_or_download(
image_location, image_id, self,
image_meta, image_service, context: cinder_context.RequestContext,
update_cache=False): volume: objects.Volume,
image_location,
image_id: str,
image_meta: Dict[str, Any],
image_service,
update_cache: bool = False) -> Optional[dict]:
# NOTE(e0ne): check for free space in image_conversion_dir before # NOTE(e0ne): check for free space in image_conversion_dir before
# image downloading. # image downloading.
# NOTE(mnaser): This check *only* happens if the backend is not able # NOTE(mnaser): This check *only* happens if the backend is not able
@ -850,7 +886,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
should_create_cache_entry = False should_create_cache_entry = False
cloned = False cloned = False
model_update = None model_update = None
if self.image_volume_cache: if self.image_volume_cache is not None:
internal_context = cinder_context.get_internal_tenant_context() internal_context = cinder_context.get_internal_tenant_context()
if not internal_context: if not internal_context:
LOG.info('Unable to get Cinder internal context, will ' LOG.info('Unable to get Cinder internal context, will '
@ -968,9 +1004,14 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
return model_update return model_update
@utils.retry(exception.SnapshotLimitReached, retries=1) @utils.retry(exception.SnapshotLimitReached, retries=1)
def _create_from_image(self, context, volume, def _create_from_image(self,
image_location, image_id, image_meta, context: cinder_context.RequestContext,
image_service, **kwargs): volume: objects.Volume,
image_location,
image_id: str,
image_meta: Dict[str, Any],
image_service,
**kwargs: Any) -> Optional[dict]:
LOG.debug("Cloning %(volume_id)s from image %(image_id)s " LOG.debug("Cloning %(volume_id)s from image %(image_id)s "
" at location %(image_location)s.", " at location %(image_location)s.",
{'volume_id': volume.id, {'volume_id': volume.id,
@ -1036,9 +1077,14 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
self._handle_bootable_volume_glance_meta(context, volume, self._handle_bootable_volume_glance_meta(context, volume,
image_id=image_id, image_id=image_id,
image_meta=image_meta) image_meta=image_meta)
typing.cast(dict, model_update)
return model_update return model_update
def _create_from_backup(self, context, volume, backup_id, **kwargs): def _create_from_backup(self,
context: cinder_context.RequestContext,
volume: objects.Volume,
backup_id: str,
**kwargs) -> Tuple[Dict, bool]:
LOG.info("Creating volume %(volume_id)s from backup %(backup_id)s.", LOG.info("Creating volume %(volume_id)s from backup %(backup_id)s.",
{'volume_id': volume.id, {'volume_id': volume.id,
'backup_id': backup_id}) 'backup_id': backup_id})
@ -1077,7 +1123,10 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
'backup_id': backup_id}) 'backup_id': backup_id})
return ret, need_update_volume return ret, need_update_volume
def _create_raw_volume(self, context, volume, **kwargs): def _create_raw_volume(self,
context: cinder_context.RequestContext,
volume: objects.Volume,
**kwargs: Any):
try: try:
ret = self.driver.create_volume(volume) ret = self.driver.create_volume(volume)
except Exception as ex: except Exception as ex:
@ -1092,7 +1141,10 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
self._cleanup_cg_in_volume(volume) self._cleanup_cg_in_volume(volume)
return ret return ret
def execute(self, context, volume, volume_spec): def execute(self,
context: cinder_context.RequestContext,
volume: objects.Volume,
volume_spec) -> dict:
volume_spec = dict(volume_spec) volume_spec = dict(volume_spec)
volume_id = volume_spec.pop('volume_id', None) volume_id = volume_spec.pop('volume_id', None)
if not volume_id: if not volume_id:
@ -1119,6 +1171,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
"with specification: %(volume_spec)s", "with specification: %(volume_spec)s",
{'volume_spec': volume_spec, 'volume_id': volume_id, {'volume_spec': volume_spec, 'volume_id': volume_id,
'create_type': create_type}) 'create_type': create_type})
model_update: dict
if create_type == 'raw': if create_type == 'raw':
model_update = self._create_raw_volume( model_update = self._create_raw_volume(
context, volume, **volume_spec) context, volume, **volume_spec)
@ -1155,7 +1208,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
raise raise
return volume_spec return volume_spec
def _cleanup_cg_in_volume(self, volume): def _cleanup_cg_in_volume(self, volume: objects.Volume) -> None:
# NOTE(xyang): Cannot have both group_id and consistencygroup_id. # NOTE(xyang): Cannot have both group_id and consistencygroup_id.
# consistencygroup_id needs to be removed to avoid DB reference # consistencygroup_id needs to be removed to avoid DB reference
# error because there isn't an entry in the consistencygroups table. # error because there isn't an entry in the consistencygroups table.

View File

@ -875,7 +875,7 @@ class VolumeManager(manager.CleanableManager,
context: context.RequestContext, context: context.RequestContext,
volume: objects.volume.Volume, volume: objects.volume.Volume,
unmanage_only=False, unmanage_only=False,
cascade=False) -> None: cascade=False):
"""Deletes and unexports volume. """Deletes and unexports volume.
1. Delete a volume(normal case) 1. Delete a volume(normal case)
@ -1277,7 +1277,7 @@ class VolumeManager(manager.CleanableManager,
context: context.RequestContext, context: context.RequestContext,
snapshot: objects.Snapshot, snapshot: objects.Snapshot,
unmanage_only: bool = False, unmanage_only: bool = False,
handle_quota: bool = True) -> None: handle_quota: bool = True):
"""Deletes and unexports snapshot.""" """Deletes and unexports snapshot."""
context = context.elevated() context = context.elevated()
snapshot._context = context snapshot._context = context

View File

@ -312,7 +312,8 @@ def remove_volume_type_access(context, volume_type_id, project_id):
'access.remove') 'access.remove')
def is_encrypted(context, volume_type_id): def is_encrypted(context: context.RequestContext,
volume_type_id: str) -> bool:
return get_volume_type_encryption(context, volume_type_id) is not None return get_volume_type_encryption(context, volume_type_id) is not None

View File

@ -723,8 +723,9 @@ def get_all_volume_groups(vg_name=None) -> list:
vg_name) vg_name)
def extract_availability_zones_from_volume_type(volume_type) \ def extract_availability_zones_from_volume_type(
-> Optional[list]: volume_type: Union['objects.VolumeType', dict]) \
-> Optional[List[str]]:
if not volume_type: if not volume_type:
return None return None
extra_specs = volume_type.get('extra_specs', {}) extra_specs = volume_type.get('extra_specs', {})
@ -1026,6 +1027,7 @@ def create_encryption_key(context: context.RequestContext,
raise exception.Invalid(message="Key manager error") raise exception.Invalid(message="Key manager error")
typing.cast(str, encryption_key_id) typing.cast(str, encryption_key_id)
return encryption_key_id return encryption_key_id

View File

@ -5,6 +5,8 @@ cinder/exception.py
cinder/manager.py cinder/manager.py
cinder/utils.py cinder/utils.py
cinder/volume/__init__.py cinder/volume/__init__.py
cinder/volume/flows/api/create_volume.py
cinder/volume/flows/manager/create_volume.py
cinder/volume/manager.py cinder/volume/manager.py
cinder/volume/volume_types.py cinder/volume/volume_types.py
cinder/volume/volume_utils.py cinder/volume/volume_utils.py