diff --git a/cinder/image/image_utils.py b/cinder/image/image_utils.py index 93e29dd5f90..cdd7b850227 100644 --- a/cinder/image/image_utils.py +++ b/cinder/image/image_utils.py @@ -29,6 +29,7 @@ import io import math import os import re +import shutil import tempfile from typing import ContextManager, Generator, Optional @@ -43,7 +44,6 @@ from oslo_utils import fileutils from oslo_utils import imageutils from oslo_utils import timeutils from oslo_utils import units -import psutil from cinder import context from cinder import exception @@ -1181,12 +1181,10 @@ def check_virtual_size(virtual_size: float, def check_available_space(dest: str, image_size: int, image_id: str) -> None: - # TODO(e0ne): replace psutil with shutil.disk_usage when we drop - # Python 2.7 support. if not os.path.isdir(dest): dest = os.path.dirname(dest) - free_space = psutil.disk_usage(dest).free + free_space = shutil.disk_usage(dest).free if free_space <= image_size: msg = ('There is no space on %(dest_dir)s to convert image. ' 'Requested: %(image_size)s, available: %(free_space)s.' diff --git a/cinder/tests/unit/volume/drivers/test_quobyte.py b/cinder/tests/unit/volume/drivers/test_quobyte.py index fe422fc23be..744e31e8e32 100644 --- a/cinder/tests/unit/volume/drivers/test_quobyte.py +++ b/cinder/tests/unit/volume/drivers/test_quobyte.py @@ -27,7 +27,6 @@ from oslo_concurrency import processutils as putils from oslo_utils import fileutils from oslo_utils import imageutils from oslo_utils import units -import psutil from cinder import context from cinder import db @@ -440,14 +439,15 @@ class QuobyteDriverTestCase(test.TestCase): any_order=False) mock_validate.assert_called_once_with(self.TEST_MNT_POINT) - @mock.patch.object(psutil, "disk_partitions") + @mock.patch.object(quobyte, 'psutil') def test_mount_quobyte_should_reraise_already_mounted_error(self, - part_mock): + ps_mock): """test_mount_quobyte_should_reraise_already_mounted_error Like test_mount_quobyte_should_suppress_already_mounted_error but with ensure=False. """ + part_mock = ps_mock.disk_partitions part_mock.return_value = [] # no quobyte@ devices with mock.patch.object(self._driver, '_execute') as mock_execute, \ mock.patch('oslo_utils.fileutils.ensure_tree') as mock_mkdir, \ @@ -624,7 +624,17 @@ class QuobyteDriverTestCase(test.TestCase): qb_snso_mock.assert_called_once_with(is_new_cinder_install=mock.ANY) self.assertFalse(drv.configuration.quobyte_overlay_volumes) - def test_check_for_setup_error_throws_quobyte_volume_url_not_set(self): + @mock.patch.object(quobyte, 'psutil', new=None) + def test_check_for_setup_error_throws_psutil_missing(self): + """check_for_setup_error raises if psutil not installed.""" + drv = self._driver + e = self.assertRaises(exception.VolumeDriverException, + drv.check_for_setup_error) + self.assertIn("psutil", str(e)) + + @mock.patch.object(quobyte, 'psutil') + def test_check_for_setup_error_throws_quobyte_volume_url_not_set( + self, mock_psutil): """check_for_setup_error throws if 'quobyte_volume_url' is not set.""" drv = self._driver @@ -634,7 +644,9 @@ class QuobyteDriverTestCase(test.TestCase): 'no Quobyte volume configured', drv.check_for_setup_error) - def test_check_for_setup_error_throws_client_not_installed(self): + @mock.patch.object(quobyte, 'psutil') + def test_check_for_setup_error_throws_client_not_installed( + self, mock_psutil): """check_for_setup_error throws if client is not installed.""" drv = self._driver drv._execute = mock.Mock(side_effect=OSError @@ -647,7 +659,9 @@ class QuobyteDriverTestCase(test.TestCase): check_exit_code=False, run_as_root=False) - def test_check_for_setup_error_throws_client_not_executable(self): + @mock.patch.object(quobyte, 'psutil') + def test_check_for_setup_error_throws_client_not_executable( + self, mock_psutil): """check_for_setup_error throws if client cannot be executed.""" drv = self._driver @@ -1488,9 +1502,10 @@ class QuobyteDriverTestCase(test.TestCase): drv.configuration.nas_secure_file_permissions) self.assertFalse(drv._execute_as_root) - @mock.patch.object(psutil, "disk_partitions") + @mock.patch.object(quobyte, 'psutil') @mock.patch.object(os, "stat") - def test_validate_volume_all_good_prefix_val(self, stat_mock, part_mock): + def test_validate_volume_all_good_prefix_val(self, stat_mock, ps_mock): + part_mock = ps_mock.disk_partitions part_mock.return_value = self.get_mock_partitions() drv = self._driver @@ -1507,9 +1522,10 @@ class QuobyteDriverTestCase(test.TestCase): stat_mock.assert_called_once_with(self.TEST_MNT_POINT) part_mock.assert_called_once_with(all=True) - @mock.patch.object(psutil, "disk_partitions") + @mock.patch.object(quobyte, 'psutil') @mock.patch.object(os, "stat") - def test_validate_volume_all_good_subtype_val(self, stat_mock, part_mock): + def test_validate_volume_all_good_subtype_val(self, stat_mock, ps_mock): + part_mock = ps_mock.disk_partitions part_mock.return_value = self.get_mock_partitions() part_mock.return_value[0].device = "not_quobyte" part_mock.return_value[0].fstype = "fuse.quobyte" @@ -1528,9 +1544,10 @@ class QuobyteDriverTestCase(test.TestCase): stat_mock.assert_called_once_with(self.TEST_MNT_POINT) part_mock.assert_called_once_with(all=True) - @mock.patch.object(psutil, "disk_partitions") + @mock.patch.object(quobyte, 'psutil') @mock.patch.object(os, "stat") - def test_validate_volume_mount_not_working(self, stat_mock, part_mock): + def test_validate_volume_mount_not_working(self, stat_mock, ps_mock): + part_mock = ps_mock.disk_partitions part_mock.return_value = self.get_mock_partitions() drv = self._driver @@ -1546,8 +1563,9 @@ class QuobyteDriverTestCase(test.TestCase): stat_mock.assert_called_once_with(self.TEST_MNT_POINT) part_mock.assert_called_once_with(all=True) - @mock.patch.object(psutil, "disk_partitions") - def test_validate_volume_no_mtab_entry(self, part_mock): + @mock.patch.object(quobyte, 'psutil') + def test_validate_volume_no_mtab_entry(self, ps_mock): + part_mock = ps_mock.disk_partitions part_mock.return_value = [] # no quobyte@ devices msg = ("Volume driver reported an error: " "No matching Quobyte mount entry for %(mpt)s" @@ -1560,8 +1578,9 @@ class QuobyteDriverTestCase(test.TestCase): self._driver._validate_volume, self.TEST_MNT_POINT) - @mock.patch.object(psutil, "disk_partitions") - def test_validate_volume_wrong_mount_type(self, part_mock): + @mock.patch.object(quobyte, 'psutil') + def test_validate_volume_wrong_mount_type(self, ps_mock): + part_mock = ps_mock.disk_partitions mypart = mock.Mock() mypart.device = "not-quobyte" mypart.mountpoint = self.TEST_MNT_POINT @@ -1579,8 +1598,9 @@ class QuobyteDriverTestCase(test.TestCase): self.TEST_MNT_POINT) part_mock.assert_called_once_with(all=True) - @mock.patch.object(psutil, "disk_partitions") - def test_validate_volume_stale_mount(self, part_mock): + @mock.patch.object(quobyte, 'psutil') + def test_validate_volume_stale_mount(self, ps_mock): + part_mock = ps_mock.disk_partitions part_mock.return_value = self.get_mock_partitions() drv = self._driver diff --git a/cinder/volume/drivers/quobyte.py b/cinder/volume/drivers/quobyte.py index 86071c489cf..d7f3bc22e17 100644 --- a/cinder/volume/drivers/quobyte.py +++ b/cinder/volume/drivers/quobyte.py @@ -23,7 +23,10 @@ from oslo_concurrency import processutils from oslo_config import cfg from oslo_log import log as logging from oslo_utils import fileutils -import psutil +try: + import psutil +except ImportError: + psutil = None from cinder import compute from cinder import coordination @@ -276,6 +279,10 @@ class QuobyteDriver(remotefs_drv.RemoteFSSnapDriverDistributed): "setting will be ignored.") def check_for_setup_error(self): + if psutil is None: + msg = _("The python 'psutil' module is required by this driver.") + LOG.error(msg) + raise exception.VolumeDriverException(msg) if not self.configuration.quobyte_volume_url: msg = (_("There's no Quobyte volume configured (%s). Example:" " quobyte://