diff --git a/cinder/backup/drivers/gcs.py b/cinder/backup/drivers/gcs.py index e372131c755..8a85b37a0d5 100644 --- a/cinder/backup/drivers/gcs.py +++ b/cinder/backup/drivers/gcs.py @@ -27,10 +27,16 @@ Server-centric flow is used for authentication. """ import base64 -from distutils import version import hashlib import os +try: + # For python 3.8 and later + import importlib.metadata as importlib_metadata +except ImportError: + # For everyone else + import importlib_metadata + try: from google.auth import exceptions as gexceptions from google.oauth2 import service_account @@ -49,7 +55,7 @@ from googleapiclient import http from oslo_config import cfg from oslo_log import log as logging from oslo_utils import timeutils -import pkg_resources +from packaging import version import six from cinder.backup import chunkeddriver @@ -142,6 +148,17 @@ def gcs_logger(func): return func_wrapper +def _get_dist_version(name): + """Mock-able wrapper for importlib_metadata.version() + + The module name where version() is found varies by python + version. This function makes it easier for tests to mock the + function and change the return value. + + """ + return importlib_metadata.version(name) + + @interface.backupdriver class GoogleBackupDriver(chunkeddriver.ChunkedBackupDriver): """Provides backup, restore and delete of backup objects within GCS.""" @@ -174,8 +191,8 @@ class GoogleBackupDriver(chunkeddriver.ChunkedBackupDriver): # If we have google client that support google-auth library # (v1.6.0 or higher) and all required libraries are installed use # google-auth for the credentials - dist = pkg_resources.get_distribution('google-api-python-client') - if (version.LooseVersion(dist.version) >= version.LooseVersion('1.6.0') + dist_version = _get_dist_version('google-api-python-client') + if (version.parse(dist_version) >= version.parse('1.6.0') and service_account): creds = service_account.Credentials.from_service_account_file( backup_credential) diff --git a/cinder/tests/unit/backup/drivers/test_backup_google.py b/cinder/tests/unit/backup/drivers/test_backup_google.py index f0491206080..10b4aec26ef 100644 --- a/cinder/tests/unit/backup/drivers/test_backup_google.py +++ b/cinder/tests/unit/backup/drivers/test_backup_google.py @@ -623,59 +623,59 @@ class GoogleBackupDriverTestCase(test.TestCase): self.assertEqual('none', result[0]) self.assertEqual(already_compressed_data, result[1]) - @mock.patch('pkg_resources.get_distribution') + @mock.patch.object(google_dr, '_get_dist_version') @mock.patch.object(google_dr.client.GoogleCredentials, 'from_stream') @mock.patch.object(google_dr.discovery, 'build') @mock.patch.object(google_dr, 'service_account') def test_non_google_auth_version(self, account, build, from_stream, - get_dist_mock): + get_dist_version): # Prior to v1.6.0 Google api client doesn't support google-auth library - get_dist_mock.return_value.version = '1.5.5' + get_dist_version.return_value = '1.5.5' google_dr.CONF.set_override('backup_gcs_credential_file', 'credentials_file') google_dr.GoogleBackupDriver(self.ctxt) - get_dist_mock.assert_called_once_with('google-api-python-client') + get_dist_version.assert_called_once_with('google-api-python-client') from_stream.assert_called_once_with('credentials_file') account.Credentials.from_service_account_file.assert_not_called() build.assert_called_once_with('storage', 'v1', cache_discovery=False, credentials=from_stream.return_value) - @mock.patch('pkg_resources.get_distribution') + @mock.patch.object(google_dr, '_get_dist_version') @mock.patch.object(google_dr.client.GoogleCredentials, 'from_stream') @mock.patch.object(google_dr.discovery, 'build') @mock.patch.object(google_dr, 'service_account', None) - def test_no_httplib2_auth(self, build, from_stream, get_dist_mock): + def test_no_httplib2_auth(self, build, from_stream, get_dist_version): # Google api client requires google-auth-httplib2 if not present we # use legacy credentials - get_dist_mock.return_value.version = '1.6.6' + get_dist_version.return_value = '1.6.6' google_dr.CONF.set_override('backup_gcs_credential_file', 'credentials_file') google_dr.GoogleBackupDriver(self.ctxt) - get_dist_mock.assert_called_once_with('google-api-python-client') + get_dist_version.assert_called_once_with('google-api-python-client') from_stream.assert_called_once_with('credentials_file') build.assert_called_once_with('storage', 'v1', cache_discovery=False, credentials=from_stream.return_value) - @mock.patch('pkg_resources.get_distribution') + @mock.patch.object(google_dr, '_get_dist_version') @mock.patch.object(google_dr, 'gexceptions', mock.Mock()) @mock.patch.object(google_dr.client.GoogleCredentials, 'from_stream') @mock.patch.object(google_dr.discovery, 'build') @mock.patch.object(google_dr, 'service_account') def test_google_auth_used(self, account, build, from_stream, - get_dist_mock): + get_dist_version): # Google api client requires google-auth-httplib2 if not present we # use legacy credentials - get_dist_mock.return_value.version = '1.6.6' + get_dist_version.return_value = '1.6.6' google_dr.CONF.set_override('backup_gcs_credential_file', 'credentials_file') google_dr.GoogleBackupDriver(self.ctxt) - get_dist_mock.assert_called_once_with('google-api-python-client') + get_dist_version.assert_called_once_with('google-api-python-client') from_stream.assert_not_called() create_creds = account.Credentials.from_service_account_file create_creds.assert_called_once_with('credentials_file') diff --git a/cinder/volume/drivers/dell_emc/powermax/utils.py b/cinder/volume/drivers/dell_emc/powermax/utils.py index 604c6c14121..ababfd388a9 100644 --- a/cinder/volume/drivers/dell_emc/powermax/utils.py +++ b/cinder/volume/drivers/dell_emc/powermax/utils.py @@ -21,6 +21,7 @@ import re from oslo_log import log as logging from oslo_utils import strutils from oslo_utils import units +import packaging.version import six from cinder import exception @@ -1989,8 +1990,9 @@ class PowerMaxUtils(object): :param minimum_version: minimum version allowed :returns: boolean """ - from pkg_resources import parse_version - return parse_version(version) >= parse_version(minimum_version) + checking = packaging.version.parse(version) + minimum = packaging.version.parse(minimum_version) + return checking >= minimum @staticmethod def parse_specs_from_pool_name(pool_name): diff --git a/lower-constraints.txt b/lower-constraints.txt index 263bee55b7e..cb424f53a53 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -37,6 +37,7 @@ greenlet==0.4.13 httplib2==0.9.1 idna==2.5 imagesize==1.0.0 +importlib-metadata==1.7.0 iso8601==0.1.12 Jinja2==2.10 jsonpatch==1.21 @@ -79,6 +80,7 @@ oslo.versionedobjects==1.31.2 oslo.vmware==2.35.0 oslotest==3.2.0 osprofiler==1.4.0 +packaging==20.4 paramiko==2.4.0 Paste==2.0.2 PasteDeploy==1.5.0 diff --git a/requirements.txt b/requirements.txt index 4981f312b6a..801a73d1abf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ decorator>=4.1.0 # BSD eventlet!=0.23.0,!=0.25.0,>=0.22.0 # MIT greenlet>=0.4.13 # MIT httplib2>=0.9.1 # MIT +importlib_metadata>=1.7.0;python_version<'3.8' # Apache-2.0 iso8601>=0.1.12 # MIT jsonschema>=3.2.0 # MIT keystoneauth1>=3.14.0 # Apache-2.0 @@ -30,6 +31,7 @@ oslo.upgradecheck>=0.1.0 # Apache-2.0 oslo.utils>=3.40.2 # Apache-2.0 oslo.versionedobjects>=1.31.2 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 +packaging>=20.4 paramiko>=2.4.0 # LGPLv2.1+ Paste>=2.0.2 # MIT PasteDeploy>=1.5.0 # MIT