Merge "Use modern type annotation format for collections"

This commit is contained in:
Zuul 2022-05-19 17:06:19 +00:00 committed by Gerrit Code Review
commit be71720bb9
20 changed files with 255 additions and 221 deletions

View File

@ -17,9 +17,11 @@
"""Handles all requests relating to the volume backups service.""" """Handles all requests relating to the volume backups service."""
from __future__ import annotations
from datetime import datetime from datetime import datetime
import random import random
from typing import List, Optional # noqa: H301 from typing import Optional
from eventlet import greenthread from eventlet import greenthread
from oslo_config import cfg from oslo_config import cfg
@ -123,8 +125,8 @@ class API(base.Base):
marker: Optional[str] = None, marker: Optional[str] = None,
limit: Optional[int] = None, limit: Optional[int] = None,
offset: Optional[int] = None, offset: Optional[int] = None,
sort_keys: Optional[List[str]] = None, sort_keys: Optional[list[str]] = None,
sort_dirs: Optional[List[str]] = None) -> 'objects.BackupList': sort_dirs: Optional[list[str]] = None) -> 'objects.BackupList':
context.authorize(policy.GET_ALL_POLICY) context.authorize(policy.GET_ALL_POLICY)
search_opts = search_opts or {} search_opts = search_opts or {}
@ -200,7 +202,7 @@ class API(base.Base):
raise exception.ServiceNotFound(service_id='cinder-backup') raise exception.ServiceNotFound(service_id='cinder-backup')
return backup_host return backup_host
def _list_backup_services(self) -> List['objects.Service']: def _list_backup_services(self) -> list['objects.Service']:
"""List all enabled backup services. """List all enabled backup services.
:returns: list -- hosts for services that are enabled for backup. :returns: list -- hosts for services that are enabled for backup.

View File

@ -17,8 +17,10 @@
"""RequestContext: context for requests that persist through all of cinder.""" """RequestContext: context for requests that persist through all of cinder."""
from __future__ import annotations
import copy import copy
from typing import Any, Dict, Optional # noqa: H301 from typing import Any, Optional # noqa: H301
from keystoneauth1.access import service_catalog as ksa_service_catalog from keystoneauth1.access import service_catalog as ksa_service_catalog
from keystoneauth1 import plugin from keystoneauth1 import plugin
@ -162,7 +164,7 @@ class RequestContext(context.RequestContext):
read_deleted = property(_get_read_deleted, _set_read_deleted, read_deleted = property(_get_read_deleted, _set_read_deleted,
_del_read_deleted) _del_read_deleted)
def to_dict(self) -> Dict[str, Any]: def to_dict(self) -> dict[str, Any]:
result = super(RequestContext, self).to_dict() result = super(RequestContext, self).to_dict()
result['user_id'] = self.user_id result['user_id'] = self.user_id
result['project_id'] = self.project_id result['project_id'] = self.project_id

View File

@ -10,8 +10,10 @@
# 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 __future__ import annotations
import os import os
from typing import Any, List # noqa: H301 from typing import Any
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
@ -25,7 +27,7 @@ from cinder import exception
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def _make_task_name(cls, addons: List[str] = None) -> str: 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 = ''
@ -41,11 +43,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: List[str] = None, **kwargs: Any) -> None: 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: List[str] = None) -> str: def make_name(cls, addons: list[str] = None) -> str:
return _make_task_name(cls, addons) return _make_task_name(cls, addons)

View File

@ -16,6 +16,8 @@
"""Implementation of an image service that uses Glance as the backend""" """Implementation of an image service that uses Glance as the backend"""
from __future__ import annotations # Remove when only supporting python 3.9+
import copy import copy
import itertools import itertools
import random import random
@ -23,8 +25,7 @@ import shutil
import sys import sys
import textwrap import textwrap
import time import time
from typing import (Any, Callable, Dict, Iterable, List, # noqa: H301 from typing import (Any, Callable, Iterable, NoReturn, Optional) # noqa: H301
NoReturn, Optional, Tuple) # noqa: H301
import urllib import urllib
import urllib.parse import urllib.parse
@ -93,7 +94,7 @@ _SESSION = None
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def _parse_image_ref(image_href: str) -> Tuple[str, str, bool]: def _parse_image_ref(image_href: str) -> tuple[str, str, bool]:
"""Parse an image href into composite parts. """Parse an image href into composite parts.
:param image_href: href of an image :param image_href: href of an image
@ -283,7 +284,7 @@ class GlanceImageService(object):
def detail(self, def detail(self,
context: context.RequestContext, context: context.RequestContext,
**kwargs: str) -> List[dict]: **kwargs: str) -> list[dict]:
"""Calls out to Glance for a list of detailed image information.""" """Calls out to Glance for a list of detailed image information."""
params = self._extract_query_params(kwargs) params = self._extract_query_params(kwargs)
try: try:
@ -298,7 +299,7 @@ class GlanceImageService(object):
return _images return _images
def _extract_query_params(self, params: dict) -> Dict[str, Any]: def _extract_query_params(self, params: dict) -> dict[str, Any]:
_params = {} _params = {}
accepted_params = ('filters', 'marker', 'limit', accepted_params = ('filters', 'marker', 'limit',
'sort_key', 'sort_dir') 'sort_key', 'sort_dir')
@ -310,7 +311,7 @@ class GlanceImageService(object):
def list_members(self, def list_members(self,
context: context.RequestContext, context: context.RequestContext,
image_id: str) -> List[dict]: image_id: str) -> list[dict]:
"""Returns a list of dicts with image member data.""" """Returns a list of dicts with image member data."""
try: try:
return self._client.call(context, return self._client.call(context,
@ -330,7 +331,7 @@ class GlanceImageService(object):
def show(self, def show(self,
context: context.RequestContext, context: context.RequestContext,
image_id: str) -> Dict[str, Any]: image_id: str) -> dict[str, Any]:
"""Returns a dict with image data for the given opaque image id.""" """Returns a dict with image data for the given opaque image id."""
try: try:
image = self._client.call(context, 'get', image_id) image = self._client.call(context, 'get', image_id)
@ -345,7 +346,7 @@ class GlanceImageService(object):
def get_location(self, def get_location(self,
context: context.RequestContext, context: context.RequestContext,
image_id: str) -> Tuple[Optional[str], Any]: image_id: str) -> tuple[Optional[str], Any]:
"""Get backend storage location url. """Get backend storage location url.
Returns a tuple containing the direct url and locations representing Returns a tuple containing the direct url and locations representing
@ -421,8 +422,8 @@ class GlanceImageService(object):
def create(self, def create(self,
context: context.RequestContext, context: context.RequestContext,
image_meta: Dict[str, Any], image_meta: dict[str, Any],
data=None) -> Dict[str, Any]: data=None) -> dict[str, Any]:
"""Store the image data and return the new image object.""" """Store the image data and return the new image object."""
sent_service_image_meta = self._translate_to_glance(image_meta) sent_service_image_meta = self._translate_to_glance(image_meta)
@ -497,7 +498,7 @@ class GlanceImageService(object):
def _translate_from_glance(self, def _translate_from_glance(self,
context: context.RequestContext, context: context.RequestContext,
image: Dict[str, Any]) -> dict: image: dict[str, Any]) -> dict:
"""Get image metadata from glance image. """Get image metadata from glance image.
Extract metadata from image and convert it's properties Extract metadata from image and convert it's properties
@ -536,7 +537,7 @@ class GlanceImageService(object):
return image_meta return image_meta
@staticmethod @staticmethod
def _translate_to_glance(image_meta: Dict[str, Any]) -> Dict[str, Any]: def _translate_to_glance(image_meta: dict[str, Any]) -> dict[str, Any]:
image_meta = _convert_to_string(image_meta) image_meta = _convert_to_string(image_meta)
image_meta = _remove_read_only(image_meta) image_meta = _remove_read_only(image_meta)
@ -684,7 +685,7 @@ def _translate_plain_exception(exc_value: BaseException) -> BaseException:
def get_remote_image_service(context: context.RequestContext, def get_remote_image_service(context: context.RequestContext,
image_href) -> Tuple[GlanceImageService, str]: image_href) -> tuple[GlanceImageService, str]:
"""Create an image_service and parse the id from the given image_href. """Create an image_service and parse the id from the given image_href.
The image_href param can be an href of the form The image_href param can be an href of the form

View File

@ -23,6 +23,7 @@ Some slight modifications, but at some point
we should look at maybe pushing this up to Oslo we should look at maybe pushing this up to Oslo
""" """
from __future__ import annotations # Remove when only supporting python 3.9+
import contextlib import contextlib
import errno import errno
@ -31,8 +32,7 @@ import math
import os import os
import re import re
import tempfile import tempfile
from typing import (ContextManager, Dict, Generator, # noqa: H301 from typing import ContextManager, Generator, Optional # noqa: H301
List, Optional, Tuple)
import cryptography import cryptography
from cursive import exception as cursive_exception from cursive import exception as cursive_exception
@ -152,7 +152,7 @@ def qemu_img_info(path: str,
return info return info
def get_qemu_img_version() -> Optional[List[int]]: def get_qemu_img_version() -> Optional[list[int]]:
"""The qemu-img version will be cached until the process is restarted.""" """The qemu-img version will be cached until the process is restarted."""
global QEMU_IMG_VERSION global QEMU_IMG_VERSION
@ -187,8 +187,7 @@ def _get_qemu_convert_luks_cmd(src: str,
cipher_spec: Optional[dict] = None, cipher_spec: Optional[dict] = None,
passphrase_file: Optional[str] = None, passphrase_file: Optional[str] = None,
src_passphrase_file: Optional[str] = None) \ src_passphrase_file: Optional[str] = None) \
-> List[str]: -> list[str]:
cmd = ['qemu-img', 'convert'] cmd = ['qemu-img', 'convert']
if prefix: if prefix:
@ -223,8 +222,7 @@ def _get_qemu_convert_cmd(src: str,
passphrase_file: Optional[str] = None, passphrase_file: Optional[str] = None,
compress: bool = False, compress: bool = False,
src_passphrase_file: Optional[str] = None) \ src_passphrase_file: Optional[str] = None) \
-> List[str]: -> list[str]:
if src_passphrase_file is not None: if src_passphrase_file is not None:
if passphrase_file is None: if passphrase_file is None:
message = _("Can't create unencrypted volume %(format)s " message = _("Can't create unencrypted volume %(format)s "
@ -290,7 +288,7 @@ def _get_qemu_convert_cmd(src: str,
return cmd return cmd
def _get_version_from_string(version_string: str) -> List[int]: def _get_version_from_string(version_string: str) -> list[int]:
return [int(x) for x in version_string.split('.')] return [int(x) for x in version_string.split('.')]
@ -451,7 +449,7 @@ def resize_image(source: str,
run_as_root: bool = False, run_as_root: bool = False,
file_format: Optional[str] = None) -> None: file_format: Optional[str] = None) -> None:
"""Changes the virtual size of the image.""" """Changes the virtual size of the image."""
cmd: Tuple[str, ...] cmd: tuple[str, ...]
if file_format: if file_format:
cmd = ('qemu-img', 'resize', '-f', file_format, source, '%sG' % size) cmd = ('qemu-img', 'resize', '-f', file_format, source, '%sG' % size)
else: else:
@ -922,7 +920,7 @@ def extract_targz(archive_name: str, target: str) -> None:
utils.execute('tar', '-xzf', archive_name, '-C', target) utils.execute('tar', '-xzf', archive_name, '-C', target)
def fix_vhd_chain(vhd_chain: List[str]) -> None: def fix_vhd_chain(vhd_chain: list[str]) -> None:
for child, parent in zip(vhd_chain[:-1], vhd_chain[1:]): for child, parent in zip(vhd_chain[:-1], vhd_chain[1:]):
set_vhd_parent(child, parent) set_vhd_parent(child, parent)
@ -991,7 +989,7 @@ def temporary_dir() -> ContextManager[str]:
return utils.tempdir(dir=CONF.image_conversion_dir) return utils.tempdir(dir=CONF.image_conversion_dir)
def coalesce_chain(vhd_chain: List[str]) -> str: def coalesce_chain(vhd_chain: list[str]) -> str:
for child, parent in zip(vhd_chain[:-1], vhd_chain[1:]): for child, parent in zip(vhd_chain[:-1], vhd_chain[1:]):
with temporary_dir() as directory_for_journal: with temporary_dir() as directory_for_journal:
size = get_vhd_size(child) size = get_vhd_size(child)
@ -1003,7 +1001,7 @@ def coalesce_chain(vhd_chain: List[str]) -> str:
return vhd_chain[-1] return vhd_chain[-1]
def discover_vhd_chain(directory: str) -> List[str]: def discover_vhd_chain(directory: str) -> list[str]:
counter = 0 counter = 0
chain = [] chain = []
@ -1028,7 +1026,7 @@ def replace_xenserver_image_with_coalesced_vhd(image_file: str) -> None:
os.rename(coalesced, image_file) os.rename(coalesced, image_file)
def decode_cipher(cipher_spec: str, key_size: int) -> Dict[str, str]: def decode_cipher(cipher_spec: str, key_size: int) -> dict[str, str]:
"""Decode a dm-crypt style cipher specification string """Decode a dm-crypt style cipher specification string
The assumed format being cipher-chainmode-ivmode, similar to that The assumed format being cipher-chainmode-ivmode, similar to that
@ -1060,7 +1058,7 @@ class TemporaryImages(object):
""" """
def __init__(self, image_service: glance.GlanceImageService): def __init__(self, image_service: glance.GlanceImageService):
self.temporary_images: Dict[str, dict] = {} self.temporary_images: dict[str, dict] = {}
self.image_service = image_service self.image_service = image_service
image_service.temp_images = self image_service.temp_images = self

View File

@ -12,6 +12,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 __future__ import annotations
__all__ = [ __all__ = [
'init', 'init',
'cleanup', 'cleanup',
@ -26,7 +28,7 @@ __all__ = [
] ]
import functools import functools
from typing import Tuple, Union # noqa: H301 from typing import Union
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
@ -230,7 +232,7 @@ class RPCAPI(object):
return versions[-1] return versions[-1]
def _get_cctxt(self, def _get_cctxt(self,
version: Union[str, Tuple[str, ...]] = None, version: Union[str, tuple[str, ...]] = None,
**kwargs): **kwargs):
"""Prepare client context """Prepare client context

View File

@ -17,8 +17,10 @@
Pluggable Weighing support Pluggable Weighing support
""" """
from __future__ import annotations
import abc import abc
from typing import Iterable, List, Optional # noqa: H301 from typing import Iterable, Optional # noqa: H301
from oslo_log import log as logging from oslo_log import log as logging
@ -28,7 +30,7 @@ from cinder.scheduler import base_handler
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def normalize(weight_list: List[float], def normalize(weight_list: list[float],
minval: Optional[float] = None, minval: Optional[float] = None,
maxval: Optional[float] = None) -> Iterable[float]: maxval: Optional[float] = None) -> Iterable[float]:
"""Normalize the values in a list between 0 and 1.0. """Normalize the values in a list between 0 and 1.0.
@ -95,8 +97,8 @@ class BaseWeigher(object, metaclass=abc.ABCMeta):
"""Override in a subclass to specify a weight for a specific object.""" """Override in a subclass to specify a weight for a specific object."""
def weigh_objects(self, def weigh_objects(self,
weighed_obj_list: List[WeighedObject], weighed_obj_list: list[WeighedObject],
weight_properties: dict) -> List[float]: weight_properties: dict) -> list[float]:
"""Weigh multiple objects. """Weigh multiple objects.
Override in a subclass if you need access to all objects in order Override in a subclass if you need access to all objects in order
@ -130,8 +132,8 @@ class BaseWeightHandler(base_handler.BaseHandler):
def get_weighed_objects(self, def get_weighed_objects(self,
weigher_classes: list, weigher_classes: list,
obj_list: List[WeighedObject], obj_list: list[WeighedObject],
weighing_properties: dict) -> List[WeighedObject]: weighing_properties: dict) -> list[WeighedObject]:
"""Return a sorted (descending), normalized list of WeighedObjects.""" """Return a sorted (descending), normalized list of WeighedObjects."""
if not obj_list: if not obj_list:

View File

@ -13,9 +13,11 @@
# 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 __future__ import annotations # Remove when only supporting python 3.9+
import operator import operator
import re import re
from typing import Callable, Dict # noqa: H301 from typing import Callable
import pyparsing import pyparsing
@ -168,7 +170,7 @@ class EvalTernaryOp(object):
class EvalFunction(object): class EvalFunction(object):
functions: Dict[str, Callable] = { functions: dict[str, Callable] = {
"abs": abs, "abs": abs,
"max": max, "max": max,
"min": min, "min": min,

View File

@ -20,7 +20,9 @@ You can customize this scheduler by specifying your own volume Filters and
Weighing Functions. Weighing Functions.
""" """
from typing import List, Optional # noqa: H301 from __future__ import annotations
from typing import Optional
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
@ -375,9 +377,9 @@ class FilterScheduler(driver.Scheduler):
def _get_weighted_candidates_generic_group( def _get_weighted_candidates_generic_group(
self, context: context.RequestContext, self, context: context.RequestContext,
group_spec: dict, request_spec_list: List[dict], group_spec: dict, request_spec_list: list[dict],
group_filter_properties: Optional[dict] = None, group_filter_properties: Optional[dict] = None,
filter_properties_list: Optional[List[dict]] = None) -> list: filter_properties_list: Optional[list[dict]] = None) -> list:
"""Finds backends that supports the group. """Finds backends that supports the group.
Returns a list of backends that meet the required specs, Returns a list of backends that meet the required specs,
@ -486,7 +488,7 @@ class FilterScheduler(driver.Scheduler):
def _get_weighted_candidates_by_group_type( def _get_weighted_candidates_by_group_type(
self, context: context.RequestContext, group_spec: dict, self, context: context.RequestContext, group_spec: dict,
group_filter_properties: Optional[dict] = None) \ group_filter_properties: Optional[dict] = None) \
-> List[WeighedHost]: -> list[WeighedHost]:
"""Finds backends that supports the group type. """Finds backends that supports the group type.
Returns a list of backends that meet the required specs, Returns a list of backends that meet the required specs,
@ -598,7 +600,7 @@ class FilterScheduler(driver.Scheduler):
return self._choose_top_backend_generic_group(weighed_backends) return self._choose_top_backend_generic_group(weighed_backends)
def _choose_top_backend(self, def _choose_top_backend(self,
weighed_backends: List[WeighedHost], weighed_backends: list[WeighedHost],
request_spec: dict): request_spec: dict):
top_backend = weighed_backends[0] top_backend = weighed_backends[0]
backend_state = top_backend.obj backend_state = top_backend.obj
@ -609,7 +611,7 @@ class FilterScheduler(driver.Scheduler):
def _choose_top_backend_generic_group( def _choose_top_backend_generic_group(
self, self,
weighed_backends: List[WeighedHost]) -> WeighedHost: weighed_backends: list[WeighedHost]) -> WeighedHost:
top_backend = weighed_backends[0] top_backend = weighed_backends[0]
backend_state = top_backend.obj backend_state = top_backend.obj
LOG.debug("Choosing %s", backend_state.backend_id) LOG.debug("Choosing %s", backend_state.backend_id)

View File

@ -10,7 +10,9 @@
# 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, Optional # noqa: H301 from __future__ import annotations # Remove when only supporting python 3.9+
from typing import Any, Optional # noqa: H301
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils from oslo_utils import excutils
@ -49,7 +51,7 @@ class ExtractSchedulerSpecTask(flow_utils.CinderTask):
volume: objects.Volume, volume: objects.Volume,
snapshot_id: Optional[str], snapshot_id: Optional[str],
image_id: Optional[str], image_id: Optional[str],
backup_id: Optional[str]) -> Dict[str, Any]: backup_id: Optional[str]) -> dict[str, Any]:
# Create the full request spec using the volume object. # Create the full request spec using the volume object.
# #
# NOTE(dulek): At this point, a volume can be deleted before it gets # NOTE(dulek): At this point, a volume can be deleted before it gets
@ -77,7 +79,7 @@ class ExtractSchedulerSpecTask(flow_utils.CinderTask):
volume: objects.Volume, volume: objects.Volume,
snapshot_id: Optional[str], snapshot_id: Optional[str],
image_id: Optional[str], image_id: Optional[str],
backup_id: Optional[str]) -> Dict[str, Any]: backup_id: Optional[str]) -> dict[str, Any]:
# For RPC version < 1.2 backward compatibility # For RPC version < 1.2 backward compatibility
if request_spec is None: if request_spec is None:
request_spec = self._populate_request_spec(volume, request_spec = self._populate_request_spec(volume,

View File

@ -15,11 +15,12 @@
"""Manage backends in the current zone.""" """Manage backends in the current zone."""
from __future__ import annotations
from collections import abc from collections import abc
import random import random
import typing import typing
from typing import (Any, Dict, Iterable, List, # noqa: H301 from typing import (Any, Iterable, Optional, Type, Union) # noqa: H301
Optional, Type, Union)
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
@ -163,7 +164,7 @@ class BackendState(object):
self.service = ReadOnlyDict(service) self.service = ReadOnlyDict(service)
def update_from_volume_capability(self, def update_from_volume_capability(self,
capability: Dict[str, Any], capability: dict[str, Any],
service=None) -> None: service=None) -> None:
"""Update information about a host from its volume_node info. """Update information about a host from its volume_node info.
@ -289,7 +290,7 @@ class BackendState(object):
'host': self.host}) 'host': self.host})
del self.pools[pool] del self.pools[pool]
def _append_backend_info(self, pool_cap: Dict[str, Any]) -> None: def _append_backend_info(self, pool_cap: dict[str, Any]) -> None:
# Fill backend level info to pool if needed. # Fill backend level info to pool if needed.
if not pool_cap.get('volume_backend_name', None): if not pool_cap.get('volume_backend_name', None):
pool_cap['volume_backend_name'] = self.volume_backend_name pool_cap['volume_backend_name'] = self.volume_backend_name
@ -407,7 +408,7 @@ class PoolState(BackendState):
self.pools: dict = {} self.pools: dict = {}
def update_from_volume_capability(self, def update_from_volume_capability(self,
capability: Dict[str, Any], capability: dict[str, Any],
service=None) -> None: service=None) -> None:
"""Update information about a pool from its volume_node info.""" """Update information about a pool from its volume_node info."""
LOG.debug("Updating capabilities for %s: %s", self.host, capability) LOG.debug("Updating capabilities for %s: %s", self.host, capability)
@ -470,7 +471,7 @@ class HostManager(object):
def __init__(self): def __init__(self):
self.service_states = {} # { <host|cluster>: {<service>: {cap k : v}}} self.service_states = {} # { <host|cluster>: {<service>: {cap k : v}}}
self.backend_state_map: Dict[str, BackendState] = {} self.backend_state_map: dict[str, BackendState] = {}
self.backup_service_states = {} self.backup_service_states = {}
self.filter_handler = filters.BackendFilterHandler('cinder.scheduler.' self.filter_handler = filters.BackendFilterHandler('cinder.scheduler.'
'filters') 'filters')
@ -513,7 +514,7 @@ class HostManager(object):
def _choose_backend_weighers( def _choose_backend_weighers(
self, self,
weight_cls_names: Optional[List[str]]) -> list: weight_cls_names: Optional[list[str]]) -> list:
"""Return a list of available weigher names. """Return a list of available weigher names.
This function checks input weigher names against a predefined set This function checks input weigher names against a predefined set
@ -795,7 +796,7 @@ class HostManager(object):
def get_pools(self, def get_pools(self,
context: cinder_context.RequestContext, context: cinder_context.RequestContext,
filters: Optional[dict] = None) -> List[dict]: filters: Optional[dict] = None) -> list[dict]:
"""Returns a dict of all pools on all hosts HostManager knows about.""" """Returns a dict of all pools on all hosts HostManager knows about."""
self._update_backend_state_map(context) self._update_backend_state_map(context)
@ -858,7 +859,7 @@ class HostManager(object):
capa_new: dict, capa_new: dict,
updated_pools: Iterable[dict], updated_pools: Iterable[dict],
host: str, host: str,
timestamp) -> List[dict]: timestamp) -> list[dict]:
pools = capa_new.get('pools') pools = capa_new.get('pools')
usage = [] usage = []
if pools and isinstance(pools, list): if pools and isinstance(pools, list):
@ -888,7 +889,7 @@ class HostManager(object):
def _get_pool_usage(self, def _get_pool_usage(self,
pool: dict, pool: dict,
host: str, timestamp) -> Dict[str, Any]: host: str, timestamp) -> dict[str, Any]:
total = pool["total_capacity_gb"] total = pool["total_capacity_gb"]
free = pool["free_capacity_gb"] free = pool["free_capacity_gb"]
@ -967,7 +968,7 @@ class HostManager(object):
def _notify_capacity_usage(self, def _notify_capacity_usage(self,
context: cinder_context.RequestContext, context: cinder_context.RequestContext,
usage: List[dict]) -> None: usage: list[dict]) -> None:
if usage: if usage:
for u in usage: for u in usage:
volume_utils.notify_about_capacity_usage( volume_utils.notify_about_capacity_usage(

View File

@ -25,6 +25,8 @@
should be placed in volume_utils instead. should be placed in volume_utils instead.
""" """
from __future__ import annotations # Remove when only supporting python 3.9+
from collections import OrderedDict from collections import OrderedDict
import contextlib import contextlib
import datetime import datetime
@ -42,8 +44,8 @@ import stat
import sys import sys
import tempfile import tempfile
import typing import typing
from typing import Callable, Dict, Iterable, Iterator, List # noqa: H301 from typing import Callable, Iterable, Iterator # noqa: H301
from typing import Optional, Tuple, Type, Union # noqa: H301 from typing import Optional, Type, Union # noqa: H301
import eventlet import eventlet
from eventlet import tpool from eventlet import tpool
@ -165,15 +167,15 @@ def check_exclusive_options(
raise exception.InvalidInput(reason=msg) raise exception.InvalidInput(reason=msg)
def execute(*cmd: str, **kwargs: Union[bool, str]) -> Tuple[str, str]: def execute(*cmd: str, **kwargs: Union[bool, str]) -> tuple[str, str]:
"""Convenience wrapper around oslo's execute() method.""" """Convenience wrapper around oslo's execute() method."""
if 'run_as_root' in kwargs and 'root_helper' not in kwargs: if 'run_as_root' in kwargs and 'root_helper' not in kwargs:
kwargs['root_helper'] = get_root_helper() kwargs['root_helper'] = get_root_helper()
return processutils.execute(*cmd, **kwargs) return processutils.execute(*cmd, **kwargs)
def check_ssh_injection(cmd_list: List[str]) -> None: def check_ssh_injection(cmd_list: list[str]) -> None:
ssh_injection_pattern: Tuple[str, ...] = ('`', '$', '|', '||', ';', '&', ssh_injection_pattern: tuple[str, ...] = ('`', '$', '|', '||', ';', '&',
'&&', '>', '>>', '<') '&&', '>', '>>', '<')
# Check whether injection attacks exist # Check whether injection attacks exist
@ -208,7 +210,7 @@ def check_ssh_injection(cmd_list: List[str]) -> None:
def check_metadata_properties( def check_metadata_properties(
metadata: Optional[Dict[str, str]]) -> None: metadata: Optional[dict[str, str]]) -> None:
"""Checks that the volume metadata properties are valid.""" """Checks that the volume metadata properties are valid."""
if not metadata: if not metadata:
@ -235,7 +237,7 @@ def check_metadata_properties(
def last_completed_audit_period(unit: Optional[str] = None) -> \ def last_completed_audit_period(unit: Optional[str] = None) -> \
Tuple[Union[datetime.datetime, datetime.timedelta], tuple[Union[datetime.datetime, datetime.timedelta],
Union[datetime.datetime, datetime.timedelta]]: Union[datetime.datetime, datetime.timedelta]]:
"""This method gives you the most recently *completed* audit period. """This method gives you the most recently *completed* audit period.
@ -496,7 +498,7 @@ def get_file_size(path: str) -> int:
def _get_disk_of_partition( def _get_disk_of_partition(
devpath: str, devpath: str,
st: Optional[os.stat_result] = None) -> Tuple[str, os.stat_result]: st: Optional[os.stat_result] = None) -> tuple[str, os.stat_result]:
"""Gets a disk device path and status from partition path. """Gets a disk device path and status from partition path.
Returns a disk device path from a partition device path, and stat for Returns a disk device path from a partition device path, and stat for
@ -633,9 +635,9 @@ class retry_if_exit_code(tenacity.retry_if_exception):
def retry(retry_param: Union[None, def retry(retry_param: Union[None,
Type[Exception], Type[Exception],
Tuple[Type[Exception], ...], tuple[Type[Exception], ...],
int, int,
Tuple[int, ...]], tuple[int, ...]],
interval: float = 1, interval: float = 1,
retries: int = 3, retries: int = 3,
backoff_rate: float = 2, backoff_rate: float = 2,

View File

@ -16,11 +16,12 @@
"""Handles all requests relating to volumes.""" """Handles all requests relating to volumes."""
from __future__ import annotations
import ast import ast
import collections import collections
import datetime import datetime
from typing import (Any, DefaultDict, Dict, Iterable, List, # noqa: H301 from typing import (Any, DefaultDict, Iterable, Optional, Union) # noqa: H301
Optional, Tuple, Union)
from castellan import key_manager from castellan import key_manager
from oslo_config import cfg from oslo_config import cfg
@ -139,7 +140,7 @@ class API(base.Base):
services = objects.ServiceList.get_all_by_topic(ctxt, topic) services = objects.ServiceList.get_all_by_topic(ctxt, topic)
az_data = [(s.availability_zone, s.disabled) az_data = [(s.availability_zone, s.disabled)
for s in services] for s in services]
disabled_map: Dict[str, bool] = {} disabled_map: dict[str, bool] = {}
for (az_name, disabled) in az_data: for (az_name, disabled) in az_data:
tracked_disabled = disabled_map.get(az_name, True) tracked_disabled = disabled_map.get(az_name, True)
disabled_map[az_name] = tracked_disabled and disabled disabled_map[az_name] = tracked_disabled and disabled
@ -725,8 +726,8 @@ class API(base.Base):
search_opts: Optional[dict] = None, search_opts: Optional[dict] = None,
marker: Optional[str] = None, marker: Optional[str] = None,
limit: Optional[int] = None, limit: Optional[int] = None,
sort_keys: Optional[List[str]] = None, sort_keys: Optional[list[str]] = None,
sort_dirs: Optional[List[str]] = None, sort_dirs: Optional[list[str]] = None,
offset: Optional[int] = None) -> objects.SnapshotList: offset: Optional[int] = None) -> objects.SnapshotList:
context.authorize(snapshot_policy.GET_ALL_POLICY) context.authorize(snapshot_policy.GET_ALL_POLICY)
@ -971,7 +972,7 @@ class API(base.Base):
utils.check_metadata_properties(metadata) utils.check_metadata_properties(metadata)
valid_status: Tuple[str, ...] valid_status: tuple[str, ...]
valid_status = ('available',) valid_status = ('available',)
if force or allow_in_use: if force or allow_in_use:
valid_status = ('available', 'in-use') valid_status = ('available', 'in-use')
@ -1118,7 +1119,7 @@ class API(base.Base):
context: context.RequestContext, context: context.RequestContext,
volume_list: objects.VolumeList) -> list: volume_list: objects.VolumeList) -> list:
reserve_opts_list = [] reserve_opts_list = []
total_reserve_opts: Dict[str, int] = {} total_reserve_opts: dict[str, int] = {}
try: try:
for volume in volume_list: for volume in volume_list:
if CONF.no_snapshot_gb_quota: if CONF.no_snapshot_gb_quota:
@ -1155,7 +1156,7 @@ class API(base.Base):
name: str, name: str,
description: str, description: str,
cgsnapshot_id: str, cgsnapshot_id: str,
group_snapshot_id: Optional[str] = None) -> Dict[str, Any]: group_snapshot_id: Optional[str] = None) -> dict[str, Any]:
options = {'volume_id': volume['id'], options = {'volume_id': volume['id'],
'cgsnapshot_id': cgsnapshot_id, 'cgsnapshot_id': cgsnapshot_id,
'group_snapshot_id': group_snapshot_id, 'group_snapshot_id': group_snapshot_id,
@ -1176,7 +1177,7 @@ class API(base.Base):
volume: objects.Volume, volume: objects.Volume,
name: str, name: str,
description: str, description: str,
metadata: Optional[Dict[str, Any]] = None, metadata: Optional[dict[str, Any]] = None,
cgsnapshot_id: Optional[str] = None, cgsnapshot_id: Optional[str] = None,
group_snapshot_id: Optional[str] = None, group_snapshot_id: Optional[str] = None,
allow_in_use: bool = False) -> objects.Snapshot: allow_in_use: bool = False) -> objects.Snapshot:
@ -1193,7 +1194,7 @@ class API(base.Base):
volume: objects.Volume, volume: objects.Volume,
name: str, name: str,
description: str, description: str,
metadata: Optional[Dict[str, Any]] = None) -> objects.Snapshot: metadata: Optional[dict[str, Any]] = None) -> objects.Snapshot:
result = self._create_snapshot(context, volume, name, description, result = self._create_snapshot(context, volume, name, description,
True, metadata) True, metadata)
LOG.info("Snapshot force create request issued successfully.", LOG.info("Snapshot force create request issued successfully.",
@ -1211,7 +1212,7 @@ class API(base.Base):
snapshot.assert_not_frozen() snapshot.assert_not_frozen()
# Build required conditions for conditional update # Build required conditions for conditional update
expected: Dict[str, Any] = {'cgsnapshot_id': None, expected: dict[str, Any] = {'cgsnapshot_id': None,
'group_snapshot_id': None} 'group_snapshot_id': None}
# If not force deleting we have status conditions # If not force deleting we have status conditions
if not force: if not force:
@ -1237,7 +1238,7 @@ class API(base.Base):
def update_snapshot(self, def update_snapshot(self,
context: context.RequestContext, context: context.RequestContext,
snapshot: objects.Snapshot, snapshot: objects.Snapshot,
fields: Dict[str, Any]) -> None: fields: dict[str, Any]) -> None:
context.authorize(snapshot_policy.UPDATE_POLICY, context.authorize(snapshot_policy.UPDATE_POLICY,
target_obj=snapshot) target_obj=snapshot)
snapshot.update(fields) snapshot.update(fields)
@ -1256,7 +1257,7 @@ class API(base.Base):
def create_volume_metadata(self, def create_volume_metadata(self,
context: context.RequestContext, context: context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
metadata: Dict[str, Any]) -> dict: metadata: dict[str, Any]) -> dict:
"""Creates volume metadata.""" """Creates volume metadata."""
context.authorize(vol_meta_policy.CREATE_POLICY, target_obj=volume) context.authorize(vol_meta_policy.CREATE_POLICY, target_obj=volume)
db_meta = self._update_volume_metadata(context, volume, metadata) db_meta = self._update_volume_metadata(context, volume, metadata)
@ -1284,7 +1285,7 @@ class API(base.Base):
def _update_volume_metadata(self, def _update_volume_metadata(self,
context: context.RequestContext, context: context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
metadata: Dict[str, Any], metadata: dict[str, Any],
delete: bool = False, delete: bool = False,
meta_type=common.METADATA_TYPES.user) -> dict: meta_type=common.METADATA_TYPES.user) -> dict:
if volume['status'] in ('maintenance', 'uploading'): if volume['status'] in ('maintenance', 'uploading'):
@ -1298,7 +1299,7 @@ class API(base.Base):
def update_volume_metadata(self, def update_volume_metadata(self,
context: context.RequestContext, context: context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
metadata: Dict[str, Any], metadata: dict[str, Any],
delete: bool = False, delete: bool = False,
meta_type=common.METADATA_TYPES.user) -> dict: meta_type=common.METADATA_TYPES.user) -> dict:
"""Updates volume metadata. """Updates volume metadata.
@ -1320,7 +1321,7 @@ class API(base.Base):
def update_volume_admin_metadata(self, def update_volume_admin_metadata(self,
context: context.RequestContext, context: context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
metadata: Dict[str, Any], metadata: dict[str, Any],
delete: Optional[bool] = False, delete: Optional[bool] = False,
add: Optional[bool] = True, add: Optional[bool] = True,
update: Optional[bool] = True) -> dict: update: Optional[bool] = True) -> dict:
@ -1369,7 +1370,7 @@ class API(base.Base):
def update_snapshot_metadata(self, def update_snapshot_metadata(self,
context: context.RequestContext, context: context.RequestContext,
snapshot: objects.Snapshot, snapshot: objects.Snapshot,
metadata: Dict[str, Any], metadata: dict[str, Any],
delete: bool = False) -> dict: delete: bool = False) -> dict:
"""Updates or creates snapshot metadata. """Updates or creates snapshot metadata.
@ -1410,7 +1411,7 @@ class API(base.Base):
def get_volume_image_metadata(self, def get_volume_image_metadata(self,
context: context.RequestContext, context: context.RequestContext,
volume: objects.Volume) -> Dict[str, str]: volume: objects.Volume) -> dict[str, str]:
context.authorize(vol_meta_policy.GET_POLICY, target_obj=volume) context.authorize(vol_meta_policy.GET_POLICY, target_obj=volume)
db_data = self.db.volume_glance_metadata_get(context, volume['id']) db_data = self.db.volume_glance_metadata_get(context, volume['id'])
LOG.info("Get volume image-metadata completed successfully.", LOG.info("Get volume image-metadata completed successfully.",
@ -1420,7 +1421,7 @@ class API(base.Base):
def get_list_volumes_image_metadata( def get_list_volumes_image_metadata(
self, self,
context: context.RequestContext, context: context.RequestContext,
volume_id_list: List[str]) -> DefaultDict[str, str]: volume_id_list: list[str]) -> DefaultDict[str, str]:
db_data = self.db.volume_glance_metadata_list_get(context, db_data = self.db.volume_glance_metadata_list_get(context,
volume_id_list) volume_id_list)
results: collections.defaultdict = collections.defaultdict(dict) results: collections.defaultdict = collections.defaultdict(dict)
@ -1458,8 +1459,8 @@ class API(base.Base):
def copy_volume_to_image(self, def copy_volume_to_image(self,
context: context.RequestContext, context: context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
metadata: Dict[str, str], metadata: dict[str, str],
force: bool) -> Dict[str, Optional[str]]: force: bool) -> dict[str, Optional[str]]:
"""Create a new image from the specified volume.""" """Create a new image from the specified volume."""
if not CONF.enable_force_upload and force: if not CONF.enable_force_upload and force:
LOG.info("Force upload to image is disabled, " LOG.info("Force upload to image is disabled, "
@ -2069,8 +2070,8 @@ class API(base.Base):
marker: Optional[str] = None, marker: Optional[str] = None,
limit: Optional[int] = None, limit: Optional[int] = None,
offset: Optional[int] = None, offset: Optional[int] = None,
sort_keys: Optional[List[str]] = None, sort_keys: Optional[list[str]] = None,
sort_dirs: Optional[List[str]] = None): sort_dirs: Optional[list[str]] = None):
svc = self._get_service_by_host_cluster(context, host, cluster_name) svc = self._get_service_by_host_cluster(context, host, cluster_name)
return self.volume_rpcapi.get_manageable_volumes(context, svc, return self.volume_rpcapi.get_manageable_volumes(context, svc,
marker, limit, marker, limit,
@ -2110,8 +2111,8 @@ class API(base.Base):
marker: Optional[str] = None, marker: Optional[str] = None,
limit: Optional[int] = None, limit: Optional[int] = None,
offset: Optional[int] = None, offset: Optional[int] = None,
sort_keys: Optional[List[str]] = None, sort_keys: Optional[list[str]] = None,
sort_dirs: Optional[List[str]] = None) -> List[dict]: sort_dirs: Optional[list[str]] = None) -> list[dict]:
svc = self._get_service_by_host_cluster(context, host, cluster_name, svc = self._get_service_by_host_cluster(context, host, cluster_name,
'snapshot') 'snapshot')
return self.volume_rpcapi.get_manageable_snapshots(context, svc, return self.volume_rpcapi.get_manageable_snapshots(context, svc,

View File

@ -13,6 +13,8 @@
# under the License. # under the License.
"""RADOS Block Device Driver""" """RADOS Block Device Driver"""
from __future__ import annotations
import binascii import binascii
import errno import errno
import json import json
@ -20,7 +22,7 @@ import math
import os import os
import tempfile import tempfile
import typing import typing
from typing import Any, Dict, List, Optional, Tuple, Union # noqa: H301 from typing import Any, Optional, Union # noqa: H301
import urllib.parse import urllib.parse
from castellan import key_manager from castellan import key_manager
@ -163,7 +165,7 @@ class RBDVolumeProxy(object):
pool: Optional[str] = None, pool: Optional[str] = None,
snapshot: Optional[str] = None, snapshot: Optional[str] = None,
read_only: bool = False, read_only: bool = False,
remote: Optional[Dict[str, str]] = None, remote: Optional[dict[str, str]] = None,
timeout: Optional[int] = None, timeout: Optional[int] = None,
client: 'rados.Rados' = None, client: 'rados.Rados' = None,
ioctx: 'rados.Ioctx' = None): ioctx: 'rados.Ioctx' = None):
@ -254,7 +256,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
**kwargs) -> None: **kwargs) -> None:
super(RBDDriver, self).__init__(*args, **kwargs) super(RBDDriver, self).__init__(*args, **kwargs)
self.configuration.append_config_values(RBD_OPTS) self.configuration.append_config_values(RBD_OPTS)
self._stats: Dict[str, Union[str, bool]] = {} self._stats: dict[str, Union[str, bool]] = {}
# allow overrides for testing # allow overrides for testing
self.rados = kwargs.get('rados', rados) self.rados = kwargs.get('rados', rados)
self.rbd = kwargs.get('rbd', rbd) self.rbd = kwargs.get('rbd', rbd)
@ -270,10 +272,10 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
self._backend_name = (self.configuration.volume_backend_name or self._backend_name = (self.configuration.volume_backend_name or
self.__class__.__name__) self.__class__.__name__)
self._active_backend_id: Optional[str] = active_backend_id self._active_backend_id: Optional[str] = active_backend_id
self._active_config: Dict[str, str] = {} self._active_config: dict[str, str] = {}
self._is_replication_enabled = False self._is_replication_enabled = False
self._replication_targets: list = [] self._replication_targets: list = []
self._target_names: List[str] = [] self._target_names: list[str] = []
self._clone_v2_api_checked: bool = False self._clone_v2_api_checked: bool = False
if self.rbd is not None: if self.rbd is not None:
@ -341,7 +343,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
' performance, fewer deletion issues') ' performance, fewer deletion issues')
def _get_target_config(self, def _get_target_config(self,
target_id: Optional[str]) -> Dict[str, target_id: Optional[str]) -> dict[str,
str]: str]:
"""Get a replication target from known replication targets.""" """Get a replication target from known replication targets."""
for target in self._replication_targets: for target in self._replication_targets:
@ -371,7 +373,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
self._target_names.append('default') self._target_names.append('default')
def _parse_replication_configs(self, def _parse_replication_configs(self,
replication_devices: List[dict]) -> None: replication_devices: list[dict]) -> None:
for replication_device in replication_devices: for replication_device in replication_devices:
if 'backend_id' not in replication_device: if 'backend_id' not in replication_device:
msg = _('Missing backend_id in replication_device ' msg = _('Missing backend_id in replication_device '
@ -396,8 +398,8 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def _get_config_tuple( def _get_config_tuple(
self, self,
remote: Optional[Dict[str, str]] = None) \ remote: Optional[dict[str, str]] = None) \
-> Tuple[Optional[str], Optional[str], -> tuple[Optional[str], Optional[str],
Optional[str], Optional[str]]: Optional[str], Optional[str]]:
if not remote: if not remote:
remote = self._active_config remote = self._active_config
@ -486,7 +488,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def RBDProxy(self) -> tpool.Proxy: def RBDProxy(self) -> tpool.Proxy:
return tpool.Proxy(self.rbd.RBD()) return tpool.Proxy(self.rbd.RBD())
def _ceph_args(self) -> List[str]: def _ceph_args(self) -> list[str]:
args = [] args = []
name, conf, user, secret_uuid = self._get_config_tuple() name, conf, user, secret_uuid = self._get_config_tuple()
@ -504,13 +506,13 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
pool: Optional[str] = None, pool: Optional[str] = None,
remote: Optional[dict] = None, remote: Optional[dict] = None,
timeout: Optional[int] = None) -> \ timeout: Optional[int] = None) -> \
Tuple['rados.Rados', 'rados.Ioctx']: tuple['rados.Rados', 'rados.Ioctx']:
@utils.retry(exception.VolumeBackendAPIException, @utils.retry(exception.VolumeBackendAPIException,
self.configuration.rados_connection_interval, self.configuration.rados_connection_interval,
self.configuration.rados_connection_retries) self.configuration.rados_connection_retries)
def _do_conn(pool: Optional[str], def _do_conn(pool: Optional[str],
remote: Optional[dict], remote: Optional[dict],
timeout: Optional[int]) -> Tuple['rados.Rados', timeout: Optional[int]) -> tuple['rados.Rados',
'rados.Ioctx']: 'rados.Ioctx']:
name, conf, user, secret_uuid = self._get_config_tuple(remote) name, conf, user, secret_uuid = self._get_config_tuple(remote)
@ -557,7 +559,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
client.shutdown() client.shutdown()
@staticmethod @staticmethod
def _get_backup_snaps(rbd_image) -> List: def _get_backup_snaps(rbd_image) -> list:
"""Get list of any backup snapshots that exist on this volume. """Get list of any backup snapshots that exist on this volume.
There should only ever be one but accept all since they need to be There should only ever be one but accept all since they need to be
@ -570,7 +572,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
from cinder.backup.drivers import ceph from cinder.backup.drivers import ceph
return ceph.CephBackupDriver.get_backup_snaps(rbd_image) return ceph.CephBackupDriver.get_backup_snaps(rbd_image)
def _get_mon_addrs(self) -> Tuple[List[str], List[str]]: def _get_mon_addrs(self) -> tuple[list[str], list[str]]:
args = ['ceph', 'mon', 'dump', '--format=json'] args = ['ceph', 'mon', 'dump', '--format=json']
args.extend(self._ceph_args()) args.extend(self._ceph_args())
out, _ = self._execute(*args) out, _ = self._execute(*args)
@ -578,7 +580,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
if lines[0].startswith('dumped monmap epoch'): if lines[0].startswith('dumped monmap epoch'):
lines = lines[1:] lines = lines[1:]
monmap = json.loads('\n'.join(lines)) monmap = json.loads('\n'.join(lines))
addrs: List[str] = [mon['addr'] for mon in monmap['mons']] addrs: list[str] = [mon['addr'] for mon in monmap['mons']]
hosts = [] hosts = []
ports = [] ports = []
for addr in addrs: for addr in addrs:
@ -614,8 +616,8 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
total_provisioned = math.ceil(float(total_provisioned) / units.Gi) total_provisioned = math.ceil(float(total_provisioned) / units.Gi)
return total_provisioned return total_provisioned
def _get_pool_stats(self) -> Union[Tuple[str, str], def _get_pool_stats(self) -> Union[tuple[str, str],
Tuple[float, float]]: tuple[float, float]]:
"""Gets pool free and total capacity in GiB. """Gets pool free and total capacity in GiB.
Calculate free and total capacity of the pool based on the pool's Calculate free and total capacity of the pool based on the pool's
@ -753,7 +755,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def create_cloned_volume( def create_cloned_volume(
self, self,
volume: Volume, volume: Volume,
src_vref: Volume) -> Optional[Dict[str, Optional[str]]]: src_vref: Volume) -> Optional[dict[str, Optional[str]]]:
"""Create a cloned volume from another volume. """Create a cloned volume from another volume.
Since we are cloning from a volume and not a snapshot, we must first Since we are cloning from a volume and not a snapshot, we must first
@ -860,7 +862,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
LOG.debug("clone created successfully") LOG.debug("clone created successfully")
return volume_update return volume_update
def _enable_replication(self, volume: Volume) -> Dict[str, str]: def _enable_replication(self, volume: Volume) -> dict[str, str]:
"""Enable replication for a volume. """Enable replication for a volume.
Returns required volume update. Returns required volume update.
@ -884,7 +886,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
return {'replication_status': fields.ReplicationStatus.ENABLED, return {'replication_status': fields.ReplicationStatus.ENABLED,
'replication_driver_data': driver_data} 'replication_driver_data': driver_data}
def _enable_multiattach(self, volume: Volume) -> Dict[str, str]: def _enable_multiattach(self, volume: Volume) -> dict[str, str]:
vol_name = utils.convert_str(volume.name) vol_name = utils.convert_str(volume.name)
with RBDVolumeProxy(self, vol_name) as image: with RBDVolumeProxy(self, vol_name) as image:
image_features = image.features() image_features = image.features()
@ -894,7 +896,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
return {'provider_location': return {'provider_location':
self._dumps({'saved_features': image_features})} self._dumps({'saved_features': image_features})}
def _disable_multiattach(self, volume: Volume) -> Dict[str, None]: def _disable_multiattach(self, volume: Volume) -> dict[str, None]:
vol_name = utils.convert_str(volume.name) vol_name = utils.convert_str(volume.name)
with RBDVolumeProxy(self, vol_name) as image: with RBDVolumeProxy(self, vol_name) as image:
try: try:
@ -932,7 +934,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def _setup_volume( def _setup_volume(
self, self,
volume: Volume, volume: Volume,
volume_type: Optional[VolumeType] = None) -> Dict[str, volume_type: Optional[VolumeType] = None) -> dict[str,
Optional[str]]: Optional[str]]:
if volume_type: if volume_type:
@ -1030,7 +1032,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
cmd.extend(self._ceph_args()) cmd.extend(self._ceph_args())
self._execute(*cmd) self._execute(*cmd)
def create_volume(self, volume: Volume) -> Dict[str, Any]: def create_volume(self, volume: Volume) -> dict[str, Any]:
"""Creates a logical volume.""" """Creates a logical volume."""
if volume.encryption_key_id: if volume.encryption_key_id:
@ -1092,7 +1094,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
volume: Volume, volume: Volume,
src_pool: str, src_pool: str,
src_image: str, src_image: str,
src_snap: str) -> Dict[str, Optional[str]]: src_snap: str) -> dict[str, Optional[str]]:
LOG.debug('cloning %(pool)s/%(img)s@%(snap)s to %(dst)s', LOG.debug('cloning %(pool)s/%(img)s@%(snap)s to %(dst)s',
dict(pool=src_pool, img=src_image, snap=src_snap, dict(pool=src_pool, img=src_image, snap=src_snap,
dst=volume.name)) dst=volume.name))
@ -1178,8 +1180,8 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
self, self,
volume: 'rbd.Image', volume: 'rbd.Image',
volume_name: str, volume_name: str,
snap: Optional[str] = None) -> Union[Tuple[str, str, str], snap: Optional[str] = None) -> Union[tuple[str, str, str],
Tuple[None, None, None]]: tuple[None, None, None]]:
"""If volume is a clone, return its parent info. """If volume is a clone, return its parent info.
Returns a tuple of (pool, parent, snap). A snapshot may optionally be Returns a tuple of (pool, parent, snap). A snapshot may optionally be
@ -1207,7 +1209,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def _get_children_info(self, def _get_children_info(self,
volume: 'rbd.Image', volume: 'rbd.Image',
snap: Optional[str]) -> List[tuple]: snap: Optional[str]) -> list[tuple]:
"""List children for the given snapshot of a volume(image). """List children for the given snapshot of a volume(image).
Returns a list of (pool, image). Returns a list of (pool, image).
@ -1429,7 +1431,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
with RBDVolumeProxy(self, volume.name) as image: with RBDVolumeProxy(self, volume.name) as image:
image.rollback_to_snap(snapshot.name) image.rollback_to_snap(snapshot.name)
def _disable_replication(self, volume: Volume) -> Dict[str, Optional[str]]: def _disable_replication(self, volume: Volume) -> dict[str, Optional[str]]:
"""Disable replication on the given volume.""" """Disable replication on the given volume."""
vol_name = utils.convert_str(volume.name) vol_name = utils.convert_str(volume.name)
with RBDVolumeProxy(self, vol_name) as image: with RBDVolumeProxy(self, vol_name) as image:
@ -1450,18 +1452,18 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
context: context.RequestContext, context: context.RequestContext,
volume: Volume, volume: Volume,
new_type: VolumeType, new_type: VolumeType,
diff: Union[Dict[str, Dict[str, str]], Dict[str, Dict], None], diff: Union[dict[str, dict[str, str]], dict[str, dict], None],
host: Optional[Dict[str, str]]) -> Tuple[bool, dict]: host: Optional[dict[str, str]]) -> tuple[bool, dict]:
"""Retype from one volume type to another on the same backend.""" """Retype from one volume type to another on the same backend."""
return True, self._setup_volume(volume, new_type) return True, self._setup_volume(volume, new_type)
@staticmethod @staticmethod
def _dumps(obj: Dict[str, Union[bool, int]]) -> str: def _dumps(obj: dict[str, Union[bool, int]]) -> str:
return json.dumps(obj, separators=(',', ':'), sort_keys=True) return json.dumps(obj, separators=(',', ':'), sort_keys=True)
def _exec_on_volume(self, def _exec_on_volume(self,
volume_name: str, volume_name: str,
remote: Dict[str, str], remote: dict[str, str],
operation: str, operation: str,
*args: Any, *args: Any,
**kwargs: Any): **kwargs: Any):
@ -1477,9 +1479,9 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def _failover_volume(self, def _failover_volume(self,
volume: Volume, volume: Volume,
remote: Dict[str, str], remote: dict[str, str],
is_demoted: bool, is_demoted: bool,
replication_status: str) -> Dict[str, Any]: replication_status: str) -> dict[str, Any]:
"""Process failover for a volume. """Process failover for a volume.
There are 2 different cases that will return different update values There are 2 different cases that will return different update values
@ -1519,8 +1521,8 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
return error_result return error_result
def _demote_volumes(self, def _demote_volumes(self,
volumes: List[Volume], volumes: list[Volume],
until_failure: bool = True) -> List[bool]: until_failure: bool = True) -> list[bool]:
"""Try to demote volumes on the current primary cluster.""" """Try to demote volumes on the current primary cluster."""
result = [] result = []
try_demoting = True try_demoting = True
@ -1542,7 +1544,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def _get_failover_target_config( def _get_failover_target_config(
self, self,
secondary_id: Optional[str] = None) -> Tuple[str, dict]: secondary_id: Optional[str] = None) -> tuple[str, dict]:
if not secondary_id: if not secondary_id:
# In auto mode exclude failback and active # In auto mode exclude failback and active
candidates = set(self._target_names).difference( candidates = set(self._target_names).difference(
@ -1557,7 +1559,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
context: context.RequestContext, context: context.RequestContext,
volumes: list, volumes: list,
secondary_id: Optional[str] = None, secondary_id: Optional[str] = None,
groups=None) -> Tuple[str, list, list]: groups=None) -> tuple[str, list, list]:
"""Failover replicated volumes.""" """Failover replicated volumes."""
LOG.info('RBD driver failover started.') LOG.info('RBD driver failover started.')
if not self._is_replication_enabled: if not self._is_replication_enabled:
@ -1595,11 +1597,11 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def failover_host(self, def failover_host(self,
context: context.RequestContext, context: context.RequestContext,
volumes: List[Volume], volumes: list[Volume],
secondary_id: Optional[str] = None, secondary_id: Optional[str] = None,
groups: Optional[List] = None) -> Tuple[str, groups: Optional[list] = None) -> tuple[str,
List[Volume], list[Volume],
List]: list]:
"""Failover to replication target. """Failover to replication target.
This function combines calls to failover() and failover_completed() to This function combines calls to failover() and failover_completed() to
@ -1631,7 +1633,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def initialize_connection(self, def initialize_connection(self,
volume: Volume, volume: Volume,
connector: dict) -> Dict[str, Any]: connector: dict) -> dict[str, Any]:
hosts, ports = self._get_mon_addrs() hosts, ports = self._get_mon_addrs()
name, conf, user, secret_uuid = self._get_config_tuple() name, conf, user, secret_uuid = self._get_config_tuple()
data = { data = {
@ -1662,7 +1664,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
pass pass
@staticmethod @staticmethod
def _parse_location(location: str) -> List[str]: def _parse_location(location: str) -> list[str]:
prefix = 'rbd://' prefix = 'rbd://'
if not location.startswith(prefix): if not location.startswith(prefix):
reason = _('Not stored in rbd') reason = _('Not stored in rbd')
@ -1729,7 +1731,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
volume: Volume, volume: Volume,
image_location: Optional[list], image_location: Optional[list],
image_meta: dict, image_meta: dict,
image_service) -> Tuple[dict, bool]: image_service) -> tuple[dict, bool]:
if image_location: if image_location:
# Note: image_location[0] is glance image direct_url. # Note: image_location[0] is glance image direct_url.
# image_location[1] contains the list of all locations (including # image_location[1] contains the list of all locations (including
@ -1885,7 +1887,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
{'old_size': old_size, 'new_size': new_size}) {'old_size': old_size, 'new_size': new_size})
def manage_existing(self, def manage_existing(self,
volume: Volume, existing_ref: Dict[str, str]) -> None: volume: Volume, existing_ref: dict[str, str]) -> None:
"""Manages an existing image. """Manages an existing image.
Renames the image name to match the expected name for the volume. Renames the image name to match the expected name for the volume.
@ -1906,7 +1908,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def manage_existing_get_size(self, def manage_existing_get_size(self,
volume: Volume, volume: Volume,
existing_ref: Dict[str, str]) -> int: existing_ref: dict[str, str]) -> int:
"""Return size of an existing image for manage_existing. """Return size of an existing image for manage_existing.
:param volume: :param volume:
@ -1963,12 +1965,12 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
return json.loads(out) return json.loads(out)
def get_manageable_volumes(self, def get_manageable_volumes(self,
cinder_volumes: List[Dict[str, str]], cinder_volumes: list[dict[str, str]],
marker: Optional[Any], marker: Optional[Any],
limit: int, limit: int,
offset: int, offset: int,
sort_keys: List[str], sort_keys: list[str],
sort_dirs: List[str]) -> List[Dict[str, Any]]: sort_dirs: list[str]) -> list[dict[str, Any]]:
manageable_volumes = [] manageable_volumes = []
cinder_ids = [resource['id'] for resource in cinder_volumes] cinder_ids = [resource['id'] for resource in cinder_volumes]
@ -2016,11 +2018,11 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
pass pass
def update_migrated_volume(self, def update_migrated_volume(self,
ctxt: Dict, ctxt: dict,
volume: Volume, volume: Volume,
new_volume: Volume, new_volume: Volume,
original_volume_status: str) -> \ original_volume_status: str) -> \
Union[Dict[str, None], Dict[str, Optional[str]]]: Union[dict[str, None], dict[str, Optional[str]]]:
"""Return model update from RBD for migrated volume. """Return model update from RBD for migrated volume.
This method should rename the back-end volume name(id) on the This method should rename the back-end volume name(id) on the
@ -2064,7 +2066,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def migrate_volume(self, def migrate_volume(self,
context: context.RequestContext, context: context.RequestContext,
volume: Volume, volume: Volume,
host: Dict[str, Dict[str, str]]) -> Tuple[bool, None]: host: dict[str, dict[str, str]]) -> tuple[bool, None]:
refuse_to_migrate = (False, None) refuse_to_migrate = (False, None)
@ -2146,7 +2148,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def manage_existing_snapshot_get_size(self, def manage_existing_snapshot_get_size(self,
snapshot: Snapshot, snapshot: Snapshot,
existing_ref: Dict[str, Any]) -> int: existing_ref: dict[str, Any]) -> int:
"""Return size of an existing image for manage_existing. """Return size of an existing image for manage_existing.
:param snapshot: :param snapshot:
@ -2197,7 +2199,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def manage_existing_snapshot(self, def manage_existing_snapshot(self,
snapshot: Snapshot, snapshot: Snapshot,
existing_ref: Dict[str, Any]) -> None: existing_ref: dict[str, Any]) -> None:
"""Manages an existing snapshot. """Manages an existing snapshot.
Renames the snapshot name to match the expected name for the snapshot. Renames the snapshot name to match the expected name for the snapshot.
@ -2220,12 +2222,12 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
volume.protect_snap(snapshot.name) volume.protect_snap(snapshot.name)
def get_manageable_snapshots(self, def get_manageable_snapshots(self,
cinder_snapshots: List[Dict[str, str]], cinder_snapshots: list[dict[str, str]],
marker: Optional[Any], marker: Optional[Any],
limit: int, limit: int,
offset: int, offset: int,
sort_keys: List[str], sort_keys: list[str],
sort_dirs: List[str]) -> List[Dict[str, Any]]: sort_dirs: list[str]) -> list[dict[str, Any]]:
"""List manageable snapshots on RBD backend.""" """List manageable snapshots on RBD backend."""
manageable_snapshots = [] manageable_snapshots = []
cinder_snapshot_ids = [resource['id'] for resource in cinder_snapshots] cinder_snapshot_ids = [resource['id'] for resource in cinder_snapshots]
@ -2286,7 +2288,7 @@ class RBDDriver(driver.CloneableImageVD, driver.MigrateVD,
def get_backup_device(self, def get_backup_device(self,
context: context.RequestContext, context: context.RequestContext,
backup: Backup) -> Tuple[Volume, bool]: backup: Backup) -> tuple[Volume, bool]:
"""Get a backup device from an existing volume. """Get a backup device from an existing volume.
To support incremental backups on Ceph to Ceph we don't clone To support incremental backups on Ceph to Ceph we don't clone

View File

@ -10,7 +10,10 @@
# 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 __future__ import annotations # Remove when only supporting python 3.9+
from typing import Any, Optional, 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
@ -82,10 +85,10 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
@staticmethod @staticmethod
def _extract_resource(resource: Optional[dict], def _extract_resource(resource: Optional[dict],
allowed_vals: Tuple[Tuple[str, ...]], allowed_vals: tuple[tuple[str, ...]],
exc: Type[exception.CinderException], exc: Type[exception.CinderException],
resource_name: str, resource_name: str,
props: Tuple[str] = ('status',)) -> Optional[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
@ -233,7 +236,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
def _get_image_metadata(self, def _get_image_metadata(self,
context: context.RequestContext, context: context.RequestContext,
image_id: Optional[str], image_id: Optional[str],
size: int) -> Optional[Dict[str, Any]]: 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
@ -257,7 +260,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
snapshot, snapshot,
source_volume, source_volume,
group: Optional[dict], group: Optional[dict],
volume_type: Optional[Dict[str, Any]] = None) -> Tuple[List[str], volume_type: Optional[dict[str, Any]] = None) -> tuple[list[str],
bool]: bool]:
"""Extracts and returns a validated availability zone list. """Extracts and returns a validated availability zone list.
@ -358,7 +361,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
volume_type_id: str, volume_type_id: str,
snapshot: Optional[objects.Snapshot], snapshot: Optional[objects.Snapshot],
source_volume: Optional[objects.Volume], source_volume: Optional[objects.Volume],
image_metadata: Optional[Dict[str, Any]]) -> Optional[str]: 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 encryption_key_id = None
@ -442,7 +445,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
group, group,
group_snapshot, group_snapshot,
backup: Optional[dict], backup: Optional[dict],
multiattach: bool = False) -> Dict[str, Any]: multiattach: bool = False) -> dict[str, Any]:
utils.check_exclusive_options(snapshot=snapshot, utils.check_exclusive_options(snapshot=snapshot,
imageRef=image_id, imageRef=image_id,
@ -502,7 +505,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
if multiattach: if multiattach:
context.authorize(policy.MULTIATTACH_POLICY) context.authorize(policy.MULTIATTACH_POLICY)
specs: Optional[Dict] = {} 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']:
@ -560,7 +563,7 @@ class EntryCreateTask(flow_utils.CinderTask):
def execute(self, def execute(self,
context: context.RequestContext, context: context.RequestContext,
optional_args: dict, optional_args: dict,
**kwargs) -> Dict[str, Any]: **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
@ -809,8 +812,8 @@ class VolumeCastTask(flow_utils.CinderTask):
def _cast_create_volume(self, def _cast_create_volume(self,
context: context.RequestContext, context: context.RequestContext,
request_spec: Dict[str, Any], request_spec: dict[str, Any],
filter_properties: Dict) -> None: 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']

View File

@ -10,10 +10,12 @@
# 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 __future__ import annotations
import binascii import binascii
import traceback import traceback
import typing import typing
from typing import Any, Dict, List, Optional, Tuple # noqa: H301 from typing import Any, Optional # noqa: H301
from castellan import key_manager from castellan import key_manager
import os_brick.initiator.connectors import os_brick.initiator.connectors
@ -676,7 +678,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
volume_metadata) volume_metadata)
@staticmethod @staticmethod
def _extract_cinder_ids(urls: List[str]) -> List[str]: def _extract_cinder_ids(urls: list[str]) -> list[str]:
"""Process a list of location URIs from glance """Process a list of location URIs from glance
:param urls: list of glance location URIs :param urls: list of glance location URIs
@ -707,7 +709,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
context: cinder_context.RequestContext, context: cinder_context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
image_location, image_location,
image_meta: Dict[str, Any]) -> Tuple[None, bool]: 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,
@ -768,7 +770,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
context: cinder_context.RequestContext, context: cinder_context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
image_location, image_location,
image_meta: Dict[str, Any], image_meta: dict[str, Any],
image_service) -> dict: 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
@ -804,7 +806,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
internal_context: cinder_context.RequestContext, internal_context: cinder_context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
image_id: str, image_id: str,
image_meta: Dict[str, Any]) -> Tuple[None, bool]: 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.
@ -853,8 +855,8 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
volume: objects.Volume, volume: objects.Volume,
image_location: str, image_location: str,
image_id: str, image_id: str,
image_meta: Dict[str, Any], image_meta: dict[str, Any],
image_service) -> Tuple[Optional[dict], image_service) -> tuple[Optional[dict],
bool]: bool]:
assert self.image_volume_cache is not None assert self.image_volume_cache is not None
internal_context = cinder_context.get_internal_tenant_context() internal_context = cinder_context.get_internal_tenant_context()
@ -895,7 +897,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
volume: objects.Volume, volume: objects.Volume,
image_location, image_location,
image_id: str, image_id: str,
image_meta: Dict[str, Any], image_meta: dict[str, Any],
image_service, image_service,
update_cache: bool = False) -> Optional[dict]: 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
@ -1045,7 +1047,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
volume: objects.Volume, volume: objects.Volume,
image_location, image_location,
image_id: str, image_id: str,
image_meta: Dict[str, Any], image_meta: dict[str, Any],
image_service, image_service,
**kwargs: Any) -> Optional[dict]: **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 "
@ -1120,7 +1122,7 @@ class CreateVolumeFromSpecTask(flow_utils.CinderTask):
context: cinder_context.RequestContext, context: cinder_context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
backup_id: str, backup_id: str,
**kwargs) -> Tuple[Dict, bool]: **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})

View File

@ -35,10 +35,12 @@ intact.
""" """
from __future__ import annotations # Remove when only supporting Python 3.9+
import functools import functools
import time import time
import typing import typing
from typing import Any, Dict, List, Optional, Set, Tuple, Union # noqa: H301 from typing import Any, Optional, Union # noqa: H301
from castellan import key_manager from castellan import key_manager
from oslo_config import cfg from oslo_config import cfg
@ -539,7 +541,7 @@ class VolumeManager(manager.CleanableManager,
num_vols: int = 0 num_vols: int = 0
num_snaps: int = 0 num_snaps: int = 0
max_objs_num: int = 0 max_objs_num: int = 0
req_range: Union[List[int], range] = [0] req_range: Union[list[int], range] = [0]
req_limit = CONF.init_host_max_objects_retrieval or 0 req_limit = CONF.init_host_max_objects_retrieval or 0
use_batch_objects_retrieval: bool = req_limit > 0 use_batch_objects_retrieval: bool = req_limit > 0
@ -1601,7 +1603,7 @@ class VolumeManager(manager.CleanableManager,
reservations = QUOTAS.reserve(ctx, **reserve_opts) reservations = QUOTAS.reserve(ctx, **reserve_opts)
# NOTE(yikun): Skip 'snapshot_id', 'source_volid' keys to avoid # NOTE(yikun): Skip 'snapshot_id', 'source_volid' keys to avoid
# creating tmp img vol from wrong snapshot or wrong source vol. # creating tmp img vol from wrong snapshot or wrong source vol.
skip: Set[str] = {'snapshot_id', 'source_volid'} skip: set[str] = {'snapshot_id', 'source_volid'}
skip.update(self._VOLUME_CLONE_SKIP_PROPERTIES) skip.update(self._VOLUME_CLONE_SKIP_PROPERTIES)
try: try:
new_vol_values = {k: volume[k] for k in set(volume.keys()) - skip} new_vol_values = {k: volume[k] for k in set(volume.keys()) - skip}
@ -3199,7 +3201,7 @@ class VolumeManager(manager.CleanableManager,
return vol_ref return vol_ref
def _get_cluster_or_host_filters(self) -> Dict[str, Any]: def _get_cluster_or_host_filters(self) -> dict[str, Any]:
if self.cluster: if self.cluster:
filters = {'cluster_name': self.cluster} filters = {'cluster_name': self.cluster}
else: else:
@ -3500,12 +3502,12 @@ class VolumeManager(manager.CleanableManager,
self, self,
context: context.RequestContext, context: context.RequestContext,
group: objects.Group, group: objects.Group,
volumes: List[objects.Volume], volumes: list[objects.Volume],
group_snapshot: Optional[objects.GroupSnapshot] = None, group_snapshot: Optional[objects.GroupSnapshot] = None,
snapshots: Optional[List[objects.Snapshot]] = None, snapshots: Optional[list[objects.Snapshot]] = None,
source_group: Optional[objects.Group] = None, source_group: Optional[objects.Group] = None,
source_vols: Optional[List[objects.Volume]] = None) \ source_vols: Optional[list[objects.Volume]] = None) \
-> Tuple[Dict[str, str], List[Dict[str, str]]]: -> tuple[dict[str, str], list[dict[str, str]]]:
"""Creates a group from source. """Creates a group from source.
:param context: the context of the caller. :param context: the context of the caller.
@ -3518,7 +3520,7 @@ class VolumeManager(manager.CleanableManager,
:returns: model_update, volumes_model_update :returns: model_update, volumes_model_update
""" """
model_update = {'status': 'available'} model_update = {'status': 'available'}
volumes_model_update: List[dict] = [] volumes_model_update: list[dict] = []
for vol in volumes: for vol in volumes:
if snapshots: if snapshots:
for snapshot in snapshots: for snapshot in snapshots:
@ -3819,7 +3821,7 @@ class VolumeManager(manager.CleanableManager,
def _convert_group_to_cg( def _convert_group_to_cg(
self, self,
group: objects.Group, group: objects.Group,
volumes: objects.VolumeList) -> Tuple[objects.Group, volumes: objects.VolumeList) -> tuple[objects.Group,
objects.VolumeList]: objects.VolumeList]:
if not group: if not group:
return None, None return None, None
@ -3832,7 +3834,7 @@ class VolumeManager(manager.CleanableManager,
return cg, volumes return cg, volumes
def _remove_consistencygroup_id_from_volumes( def _remove_consistencygroup_id_from_volumes(
self, volumes: Optional[List[objects.Volume]]) -> None: self, volumes: Optional[list[objects.Volume]]) -> None:
if not volumes: if not volumes:
return return
for vol in volumes: for vol in volumes:
@ -3843,7 +3845,7 @@ class VolumeManager(manager.CleanableManager,
self, self,
group_snapshot: objects.GroupSnapshot, group_snapshot: objects.GroupSnapshot,
snapshots: objects.SnapshotList, snapshots: objects.SnapshotList,
ctxt) -> Tuple[objects.CGSnapshot, ctxt) -> tuple[objects.CGSnapshot,
objects.SnapshotList]: objects.SnapshotList]:
if not group_snapshot: if not group_snapshot:
return None, None return None, None
@ -3881,7 +3883,7 @@ class VolumeManager(manager.CleanableManager,
def _delete_group_generic(self, def _delete_group_generic(self,
context: context.RequestContext, context: context.RequestContext,
group: objects.Group, group: objects.Group,
volumes) -> Tuple: volumes) -> tuple:
"""Deletes a group and volumes in the group.""" """Deletes a group and volumes in the group."""
model_update = {'status': group.status} model_update = {'status': group.status}
volume_model_updates = [] volume_model_updates = []
@ -3903,7 +3905,7 @@ class VolumeManager(manager.CleanableManager,
def _update_group_generic( def _update_group_generic(
self, context: context.RequestContext, group, self, context: context.RequestContext, group,
add_volumes=None, add_volumes=None,
remove_volumes=None) -> Tuple[None, None, None]: remove_volumes=None) -> tuple[None, None, None]:
"""Updates a group.""" """Updates a group."""
# NOTE(xyang): The volume manager adds/removes the volume to/from the # NOTE(xyang): The volume manager adds/removes the volume to/from the
# group in the database. This default implementation does not do # group in the database. This default implementation does not do
@ -3916,7 +3918,7 @@ class VolumeManager(manager.CleanableManager,
group, group,
volumes: Optional[str], volumes: Optional[str],
add: bool = True) -> list: add: bool = True) -> list:
valid_status: Tuple[str, ...] valid_status: tuple[str, ...]
if add: if add:
valid_status = VALID_ADD_VOL_TO_GROUP_STATUS valid_status = VALID_ADD_VOL_TO_GROUP_STATUS
else: else:
@ -4182,7 +4184,7 @@ class VolumeManager(manager.CleanableManager,
self, self,
context: context.RequestContext, context: context.RequestContext,
group_snapshot: objects.GroupSnapshot, group_snapshot: objects.GroupSnapshot,
snapshots: list) -> Tuple[dict, List[dict]]: snapshots: list) -> tuple[dict, list[dict]]:
"""Creates a group_snapshot.""" """Creates a group_snapshot."""
model_update = {'status': 'available'} model_update = {'status': 'available'}
snapshot_model_updates = [] snapshot_model_updates = []
@ -4208,7 +4210,7 @@ class VolumeManager(manager.CleanableManager,
self, self,
context: context.RequestContext, context: context.RequestContext,
group_snapshot: objects.GroupSnapshot, group_snapshot: objects.GroupSnapshot,
snapshots: list) -> Tuple[dict, List[dict]]: snapshots: list) -> tuple[dict, list[dict]]:
"""Deletes a group_snapshot.""" """Deletes a group_snapshot."""
model_update = {'status': group_snapshot.status} model_update = {'status': group_snapshot.status}
snapshot_model_updates = [] snapshot_model_updates = []
@ -4772,7 +4774,7 @@ class VolumeManager(manager.CleanableManager,
ctxt: context.RequestContext, ctxt: context.RequestContext,
volume: objects.Volume, volume: objects.Volume,
attachment: objects.VolumeAttachment, attachment: objects.VolumeAttachment,
connector: dict) -> Dict[str, Any]: connector: dict) -> dict[str, Any]:
try: try:
self.driver.validate_connector(connector) self.driver.validate_connector(connector)
except exception.InvalidConnectorException as err: except exception.InvalidConnectorException as err:
@ -4830,7 +4832,7 @@ class VolumeManager(manager.CleanableManager,
context: context.RequestContext, context: context.RequestContext,
vref: objects.Volume, vref: objects.Volume,
connector: dict, connector: dict,
attachment_id: str) -> Dict[str, Any]: attachment_id: str) -> dict[str, Any]:
"""Update/Finalize an attachment. """Update/Finalize an attachment.
This call updates a valid attachment record to associate with a volume This call updates a valid attachment record to associate with a volume
@ -5236,7 +5238,7 @@ class VolumeManager(manager.CleanableManager,
def list_replication_targets(self, def list_replication_targets(self,
ctxt: context.RequestContext, ctxt: context.RequestContext,
group: objects.Group) -> Dict[str, list]: group: objects.Group) -> dict[str, list]:
"""Provide a means to obtain replication targets for a group. """Provide a means to obtain replication targets for a group.
This method is used to find the replication_device config This method is used to find the replication_device config

View File

@ -12,7 +12,9 @@
# 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 Optional, Tuple, Union # noqa: H301 from __future__ import annotations
from typing import Optional, Union # noqa: H301
from cinder.common import constants from cinder.common import constants
from cinder import context from cinder import context
@ -147,7 +149,7 @@ class VolumeAPI(rpc.RPCAPI):
def _get_cctxt(self, def _get_cctxt(self,
host: Optional[str] = None, host: Optional[str] = None,
version: Optional[Union[str, Tuple[str, ...]]] = None, version: Optional[Union[str, tuple[str, ...]]] = None,
**kwargs) -> rpc.RPCAPI: **kwargs) -> rpc.RPCAPI:
if host: if host:
server = volume_utils.extract_host(host) server = volume_utils.extract_host(host)

View File

@ -19,7 +19,9 @@
"""Built-in volume type properties.""" """Built-in volume type properties."""
import typing as ty from __future__ import annotations
from typing import Optional
from oslo_config import cfg from oslo_config import cfg
from oslo_db import exception as db_exc from oslo_db import exception as db_exc
@ -330,7 +332,7 @@ def get_volume_type_qos_specs(volume_type_id):
def volume_types_diff(context: context.RequestContext, def volume_types_diff(context: context.RequestContext,
vol_type_id1, vol_type_id1,
vol_type_id2) -> ty.Tuple[dict, bool]: vol_type_id2) -> tuple[dict, bool]:
"""Returns a 'diff' of two volume types and whether they are equal. """Returns a 'diff' of two volume types and whether they are equal.
Returns a tuple of (diff, equal), where 'equal' is a boolean indicating Returns a tuple of (diff, equal), where 'equal' is a boolean indicating
@ -370,8 +372,8 @@ def volume_types_diff(context: context.RequestContext,
encryption.pop(param, None) encryption.pop(param, None)
return encryption return encryption
def _dict_diff(dict1: ty.Optional[dict], def _dict_diff(dict1: Optional[dict],
dict2: ty.Optional[dict]) -> ty.Tuple[dict, bool]: dict2: Optional[dict]) -> tuple[dict, bool]:
res = {} res = {}
equal = True equal = True
if dict1 is None: if dict1 is None:

View File

@ -14,6 +14,8 @@
"""Volume-related Utilities and helpers.""" """Volume-related Utilities and helpers."""
from __future__ import annotations # Remove when only supporting python 3.9+
import abc import abc
import ast import ast
import functools import functools
@ -31,8 +33,8 @@ import tempfile
import time import time
import types import types
import typing import typing
from typing import Any, BinaryIO, Callable, Dict, IO # noqa: H301 from typing import Any, BinaryIO, Callable, IO # noqa: H301
from typing import List, Optional, Tuple, Union # noqa: H301 from typing import Optional, Union # noqa: H301
import uuid import uuid
from castellan.common.credentials import keystone_password from castellan.common.credentials import keystone_password
@ -242,8 +244,8 @@ def notify_about_snapshot_usage(context: context.RequestContext,
usage_info) usage_info)
def _usage_from_capacity(capacity: Dict[str, Any], def _usage_from_capacity(capacity: dict[str, Any],
**extra_usage_info) -> Dict[str, Any]: **extra_usage_info) -> dict[str, Any]:
capacity_info = { capacity_info = {
'name_to_id': capacity['name_to_id'], 'name_to_id': capacity['name_to_id'],
@ -686,7 +688,7 @@ def get_all_volume_groups(vg_name=None) -> list:
def extract_availability_zones_from_volume_type( def extract_availability_zones_from_volume_type(
volume_type: Union['objects.VolumeType', dict]) \ volume_type: Union['objects.VolumeType', dict]) \
-> Optional[List[str]]: -> 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', {})
@ -705,7 +707,7 @@ DEFAULT_PASSWORD_SYMBOLS = ('23456789', # Removed: 0,1
def generate_password( def generate_password(
length: int = 16, length: int = 16,
symbolgroups: Tuple[str, ...] = DEFAULT_PASSWORD_SYMBOLS) -> str: symbolgroups: tuple[str, ...] = DEFAULT_PASSWORD_SYMBOLS) -> str:
"""Generate a random password from the supplied symbol groups. """Generate a random password from the supplied symbol groups.
At least one symbol from each group will be included. Unpredictable At least one symbol from each group will be included. Unpredictable
@ -744,7 +746,7 @@ def generate_password(
def generate_username( def generate_username(
length: int = 20, length: int = 20,
symbolgroups: Tuple[str, ...] = DEFAULT_PASSWORD_SYMBOLS) -> str: symbolgroups: tuple[str, ...] = DEFAULT_PASSWORD_SYMBOLS) -> str:
# Use the same implementation as the password generation. # Use the same implementation as the password generation.
return generate_password(length, symbolgroups) return generate_password(length, symbolgroups)
@ -864,12 +866,12 @@ def extract_id_from_snapshot_name(snap_name: str) -> Optional[str]:
return match.group('uuid') if match else None return match.group('uuid') if match else None
def paginate_entries_list(entries: List[Dict], def paginate_entries_list(entries: list[dict],
marker: Optional[Union[dict, str]], marker: Optional[Union[dict, str]],
limit: int, limit: int,
offset: Optional[int], offset: Optional[int],
sort_keys: List[str], sort_keys: list[str],
sort_dirs: List[str]) -> list: sort_dirs: list[str]) -> list:
"""Paginate a list of entries. """Paginate a list of entries.
:param entries: list of dictionaries :param entries: list of dictionaries
@ -1096,7 +1098,7 @@ def get_max_over_subscription_ratio(
return mosr return mosr
def check_image_metadata(image_meta: Dict[str, Union[str, int]], def check_image_metadata(image_meta: dict[str, Union[str, int]],
vol_size: int) -> None: vol_size: int) -> None:
"""Validates the image metadata.""" """Validates the image metadata."""
# Check whether image is active # Check whether image is active
@ -1136,7 +1138,7 @@ def enable_bootable_flag(volume: 'objects.Volume') -> None:
def get_volume_image_metadata(image_id: str, def get_volume_image_metadata(image_id: str,
image_meta: Dict[str, Any]) -> dict: image_meta: dict[str, Any]) -> dict:
# Save some base attributes into the volume metadata # Save some base attributes into the volume metadata
base_metadata = { base_metadata = {
@ -1172,7 +1174,7 @@ def copy_image_to_volume(driver,
context: context.RequestContext, context: context.RequestContext,
volume: 'objects.Volume', volume: 'objects.Volume',
image_meta: dict, image_meta: dict,
image_location: Union[str, Tuple[Optional[str], Any]], image_location: Union[str, tuple[Optional[str], Any]],
image_service) -> None: image_service) -> None:
"""Downloads Glance image to the specified volume.""" """Downloads Glance image to the specified volume."""
image_id = image_meta['id'] image_id = image_meta['id']