summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorTim Burke <tim.burke@gmail.com>2023-04-05 20:11:38 -0700
committerTim Burke <tim.burke@gmail.com>2023-04-12 13:17:10 -0700
commitc78a5962b5f6c9e75f154cac924a226815236e98 (patch)
tree8dcc6df39642dfe7542e74022575ffadd906a314 /test
parent984cca9263c1589e1f4858ee1b0ba5d2ac1bf702 (diff)
downloadswift-c78a5962b5f6c9e75f154cac924a226815236e98.tar.gz
Pull libc-related functions out to a separate module
Partial-Bug: #2015274 Change-Id: I3e26f8d4e5de0835212ebc2314cac713950c85d7
Diffstat (limited to 'test')
-rw-r--r--test/unit/common/test_utils.py560
-rw-r--r--test/unit/common/utils/test_libc.py599
-rw-r--r--test/unit/obj/test_diskfile.py3
3 files changed, 601 insertions, 561 deletions
diff --git a/test/unit/common/test_utils.py b/test/unit/common/test_utils.py
index 10fe383da..e477ee85d 100644
--- a/test/unit/common/test_utils.py
+++ b/test/unit/common/test_utils.py
@@ -23,7 +23,6 @@ from test.debug_logger import debug_logger
from test.unit import temptree, make_timestamp_iter, with_tempdir, \
mock_timestamp_now, FakeIterable
-import ctypes
import contextlib
import errno
import eventlet
@@ -33,7 +32,6 @@ import eventlet.patcher
import functools
import grp
import logging
-import platform
import os
import mock
import posix
@@ -2679,44 +2677,6 @@ cluster_dfw1 = http://dfw1.host/v1/
ts = utils.get_trans_id_time('tx1df4ff4f55ea45f7b2ec2-almostright')
self.assertIsNone(ts)
- def test_config_fallocate_value(self):
- fallocate_value, is_percent = utils.config_fallocate_value('10%')
- self.assertEqual(fallocate_value, 10)
- self.assertTrue(is_percent)
- fallocate_value, is_percent = utils.config_fallocate_value('10')
- self.assertEqual(fallocate_value, 10)
- self.assertFalse(is_percent)
- try:
- fallocate_value, is_percent = utils.config_fallocate_value('ab%')
- except ValueError as err:
- exc = err
- self.assertEqual(str(exc), 'Error: ab% is an invalid value for '
- 'fallocate_reserve.')
- try:
- fallocate_value, is_percent = utils.config_fallocate_value('ab')
- except ValueError as err:
- exc = err
- self.assertEqual(str(exc), 'Error: ab is an invalid value for '
- 'fallocate_reserve.')
- try:
- fallocate_value, is_percent = utils.config_fallocate_value('1%%')
- except ValueError as err:
- exc = err
- self.assertEqual(str(exc), 'Error: 1%% is an invalid value for '
- 'fallocate_reserve.')
- try:
- fallocate_value, is_percent = utils.config_fallocate_value('10.0')
- except ValueError as err:
- exc = err
- self.assertEqual(str(exc), 'Error: 10.0 is an invalid value for '
- 'fallocate_reserve.')
- fallocate_value, is_percent = utils.config_fallocate_value('10.5%')
- self.assertEqual(fallocate_value, 10.5)
- self.assertTrue(is_percent)
- fallocate_value, is_percent = utils.config_fallocate_value('10.000%')
- self.assertEqual(fallocate_value, 10.000)
- self.assertTrue(is_percent)
-
def test_lock_file(self):
flags = os.O_CREAT | os.O_RDWR
with NamedTemporaryFile(delete=False) as nt:
@@ -3544,110 +3504,6 @@ cluster_dfw1 = http://dfw1.host/v1/
self.assertRaises(ValueError, utils.make_db_file_path,
'/path/to/hash.db', 'bad epoch')
- def test_modify_priority(self):
- pid = os.getpid()
- logger = debug_logger()
- called = {}
-
- def _fake_setpriority(*args):
- called['setpriority'] = args
-
- def _fake_syscall(*args):
- called['syscall'] = args
-
- # Test if current architecture supports changing of priority
- try:
- utils.NR_ioprio_set()
- except OSError as e:
- raise unittest.SkipTest(e)
-
- with patch('swift.common.utils._libc_setpriority',
- _fake_setpriority), \
- patch('swift.common.utils._posix_syscall', _fake_syscall):
- called = {}
- # not set / default
- utils.modify_priority({}, logger)
- self.assertEqual(called, {})
- called = {}
- # just nice
- utils.modify_priority({'nice_priority': '1'}, logger)
- self.assertEqual(called, {'setpriority': (0, pid, 1)})
- called = {}
- # just ionice class uses default priority 0
- utils.modify_priority({'ionice_class': 'IOPRIO_CLASS_RT'}, logger)
- architecture = os.uname()[4]
- arch_bits = platform.architecture()[0]
- if architecture == 'x86_64' and arch_bits == '64bit':
- self.assertEqual(called, {'syscall': (251, 1, pid, 1 << 13)})
- elif architecture == 'aarch64' and arch_bits == '64bit':
- self.assertEqual(called, {'syscall': (30, 1, pid, 1 << 13)})
- else:
- self.fail("Unexpected call: %r" % called)
- called = {}
- # just ionice priority is ignored
- utils.modify_priority({'ionice_priority': '4'}, logger)
- self.assertEqual(called, {})
- called = {}
- # bad ionice class
- utils.modify_priority({'ionice_class': 'class_foo'}, logger)
- self.assertEqual(called, {})
- called = {}
- # ionice class & priority
- utils.modify_priority({
- 'ionice_class': 'IOPRIO_CLASS_BE',
- 'ionice_priority': '4',
- }, logger)
- if architecture == 'x86_64' and arch_bits == '64bit':
- self.assertEqual(called, {
- 'syscall': (251, 1, pid, 2 << 13 | 4)
- })
- elif architecture == 'aarch64' and arch_bits == '64bit':
- self.assertEqual(called, {
- 'syscall': (30, 1, pid, 2 << 13 | 4)
- })
- else:
- self.fail("Unexpected call: %r" % called)
- called = {}
- # all
- utils.modify_priority({
- 'nice_priority': '-15',
- 'ionice_class': 'IOPRIO_CLASS_IDLE',
- 'ionice_priority': '6',
- }, logger)
- if architecture == 'x86_64' and arch_bits == '64bit':
- self.assertEqual(called, {
- 'setpriority': (0, pid, -15),
- 'syscall': (251, 1, pid, 3 << 13 | 6),
- })
- elif architecture == 'aarch64' and arch_bits == '64bit':
- self.assertEqual(called, {
- 'setpriority': (0, pid, -15),
- 'syscall': (30, 1, pid, 3 << 13 | 6),
- })
- else:
- self.fail("Unexpected call: %r" % called)
-
- def test__NR_ioprio_set(self):
- with patch('os.uname', return_value=('', '', '', '', 'x86_64')), \
- patch('platform.architecture', return_value=('64bit', '')):
- self.assertEqual(251, utils.NR_ioprio_set())
-
- with patch('os.uname', return_value=('', '', '', '', 'x86_64')), \
- patch('platform.architecture', return_value=('32bit', '')):
- self.assertRaises(OSError, utils.NR_ioprio_set)
-
- with patch('os.uname', return_value=('', '', '', '', 'aarch64')), \
- patch('platform.architecture', return_value=('64bit', '')):
- self.assertEqual(30, utils.NR_ioprio_set())
-
- with patch('os.uname', return_value=('', '', '', '', 'aarch64')), \
- patch('platform.architecture', return_value=('32bit', '')):
- self.assertRaises(OSError, utils.NR_ioprio_set)
-
- with patch('os.uname', return_value=('', '', '', '', 'alpha')), \
- patch('platform.architecture', return_value=('64bit', '')):
- self.assertRaises(OSError, utils.NR_ioprio_set)
-
@requires_o_tmpfile_support_in_tmp
def test_link_fd_to_path_linkat_success(self):
tempdir = mkdtemp()
@@ -8882,422 +8738,6 @@ class TestShardRangeList(unittest.TestCase):
do_test([utils.ShardRange.ACTIVE]))
-@patch('ctypes.get_errno')
-@patch.object(utils, '_sys_posix_fallocate')
-@patch.object(utils, '_sys_fallocate')
-@patch.object(utils, 'FALLOCATE_RESERVE', 0)
-class TestFallocate(unittest.TestCase):
- def test_fallocate(self, sys_fallocate_mock,
- sys_posix_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = True
- sys_fallocate_mock.return_value = 0
-
- utils.fallocate(1234, 5000 * 2 ** 20)
-
- # We can't use sys_fallocate_mock.assert_called_once_with because no
- # two ctypes.c_uint64 objects are equal even if their values are
- # equal. Yes, ctypes.c_uint64(123) != ctypes.c_uint64(123).
- calls = sys_fallocate_mock.mock_calls
- self.assertEqual(len(calls), 1)
- args = calls[0][1]
- self.assertEqual(len(args), 4)
- self.assertEqual(args[0], 1234)
- self.assertEqual(args[1], utils.FALLOC_FL_KEEP_SIZE)
- self.assertEqual(args[2].value, 0)
- self.assertEqual(args[3].value, 5000 * 2 ** 20)
-
- sys_posix_fallocate_mock.assert_not_called()
-
- def test_fallocate_offset(self, sys_fallocate_mock,
- sys_posix_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = True
- sys_fallocate_mock.return_value = 0
-
- utils.fallocate(1234, 5000 * 2 ** 20, offset=3 * 2 ** 30)
- calls = sys_fallocate_mock.mock_calls
- self.assertEqual(len(calls), 1)
- args = calls[0][1]
- self.assertEqual(len(args), 4)
- self.assertEqual(args[0], 1234)
- self.assertEqual(args[1], utils.FALLOC_FL_KEEP_SIZE)
- self.assertEqual(args[2].value, 3 * 2 ** 30)
- self.assertEqual(args[3].value, 5000 * 2 ** 20)
-
- sys_posix_fallocate_mock.assert_not_called()
-
- def test_fallocate_fatal_error(self, sys_fallocate_mock,
- sys_posix_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = True
- sys_fallocate_mock.return_value = -1
- get_errno_mock.return_value = errno.EIO
-
- with self.assertRaises(OSError) as cm:
- utils.fallocate(1234, 5000 * 2 ** 20)
- self.assertEqual(cm.exception.errno, errno.EIO)
-
- def test_fallocate_silent_errors(self, sys_fallocate_mock,
- sys_posix_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = True
- sys_fallocate_mock.return_value = -1
-
- for silent_error in (0, errno.ENOSYS, errno.EOPNOTSUPP, errno.EINVAL):
- get_errno_mock.return_value = silent_error
- try:
- utils.fallocate(1234, 5678)
- except OSError:
- self.fail("fallocate() raised an error on %d", silent_error)
-
- def test_posix_fallocate_fallback(self, sys_fallocate_mock,
- sys_posix_fallocate_mock,
- get_errno_mock):
- sys_fallocate_mock.available = False
- sys_fallocate_mock.side_effect = NotImplementedError
-
- sys_posix_fallocate_mock.available = True
- sys_posix_fallocate_mock.return_value = 0
-
- utils.fallocate(1234, 567890)
- sys_fallocate_mock.assert_not_called()
-
- calls = sys_posix_fallocate_mock.mock_calls
- self.assertEqual(len(calls), 1)
- args = calls[0][1]
- self.assertEqual(len(args), 3)
- self.assertEqual(args[0], 1234)
- self.assertEqual(args[1].value, 0)
- self.assertEqual(args[2].value, 567890)
-
- def test_posix_fallocate_offset(self, sys_fallocate_mock,
- sys_posix_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = False
- sys_fallocate_mock.side_effect = NotImplementedError
-
- sys_posix_fallocate_mock.available = True
- sys_posix_fallocate_mock.return_value = 0
-
- utils.fallocate(1234, 5000 * 2 ** 20, offset=3 * 2 ** 30)
- calls = sys_posix_fallocate_mock.mock_calls
- self.assertEqual(len(calls), 1)
- args = calls[0][1]
- self.assertEqual(len(args), 3)
- self.assertEqual(args[0], 1234)
- self.assertEqual(args[1].value, 3 * 2 ** 30)
- self.assertEqual(args[2].value, 5000 * 2 ** 20)
-
- sys_fallocate_mock.assert_not_called()
-
- def test_no_fallocates_available(self, sys_fallocate_mock,
- sys_posix_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = False
- sys_posix_fallocate_mock.available = False
-
- with mock.patch("logging.warning") as warning_mock, \
- mock.patch.object(utils, "_fallocate_warned_about_missing",
- False):
- utils.fallocate(321, 654)
- utils.fallocate(321, 654)
-
- sys_fallocate_mock.assert_not_called()
- sys_posix_fallocate_mock.assert_not_called()
- get_errno_mock.assert_not_called()
-
- self.assertEqual(len(warning_mock.mock_calls), 1)
-
- def test_arg_bounds(self, sys_fallocate_mock,
- sys_posix_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = True
- sys_fallocate_mock.return_value = 0
- with self.assertRaises(ValueError):
- utils.fallocate(0, 1 << 64, 0)
- with self.assertRaises(ValueError):
- utils.fallocate(0, 0, -1)
- with self.assertRaises(ValueError):
- utils.fallocate(0, 0, 1 << 64)
- self.assertEqual([], sys_fallocate_mock.mock_calls)
- # sanity check
- utils.fallocate(0, 0, 0)
- self.assertEqual(
- [mock.call(0, utils.FALLOC_FL_KEEP_SIZE, mock.ANY, mock.ANY)],
- sys_fallocate_mock.mock_calls)
- # Go confirm the ctypes values separately; apparently == doesn't
- # work the way you'd expect with ctypes :-/
- self.assertEqual(sys_fallocate_mock.mock_calls[0][1][2].value, 0)
- self.assertEqual(sys_fallocate_mock.mock_calls[0][1][3].value, 0)
- sys_fallocate_mock.reset_mock()
-
- # negative size will be adjusted as 0
- utils.fallocate(0, -1, 0)
- self.assertEqual(
- [mock.call(0, utils.FALLOC_FL_KEEP_SIZE, mock.ANY, mock.ANY)],
- sys_fallocate_mock.mock_calls)
- self.assertEqual(sys_fallocate_mock.mock_calls[0][1][2].value, 0)
- self.assertEqual(sys_fallocate_mock.mock_calls[0][1][3].value, 0)
-
-
-@patch.object(os, 'fstatvfs')
-@patch.object(utils, '_sys_fallocate', available=True, return_value=0)
-@patch.object(utils, 'FALLOCATE_RESERVE', 0)
-@patch.object(utils, 'FALLOCATE_IS_PERCENT', False)
-@patch.object(utils, '_fallocate_enabled', True)
-class TestFallocateReserve(unittest.TestCase):
- def _statvfs_result(self, f_frsize, f_bavail):
- # Only 3 values are relevant to us, so use zeros for the rest
- f_blocks = 100
- return posix.statvfs_result((0, f_frsize, f_blocks, 0, f_bavail,
- 0, 0, 0, 0, 0))
-
- def test_disabled(self, sys_fallocate_mock, fstatvfs_mock):
- utils.disable_fallocate()
- utils.fallocate(123, 456)
-
- sys_fallocate_mock.assert_not_called()
- fstatvfs_mock.assert_not_called()
-
- def test_zero_reserve(self, sys_fallocate_mock, fstatvfs_mock):
- utils.fallocate(123, 456)
-
- fstatvfs_mock.assert_not_called()
- self.assertEqual(len(sys_fallocate_mock.mock_calls), 1)
-
- def test_enough_space(self, sys_fallocate_mock, fstatvfs_mock):
- # Want 1024 bytes in reserve plus 1023 allocated, and have 2 blocks
- # of size 1024 free, so succeed
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('1024')
-
- fstatvfs_mock.return_value = self._statvfs_result(1024, 2)
- utils.fallocate(88, 1023)
-
- def test_not_enough_space(self, sys_fallocate_mock, fstatvfs_mock):
- # Want 1024 bytes in reserve plus 1024 allocated, and have 2 blocks
- # of size 1024 free, so fail
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('1024')
-
- fstatvfs_mock.return_value = self._statvfs_result(1024, 2)
- with self.assertRaises(OSError) as catcher:
- utils.fallocate(88, 1024)
- self.assertEqual(
- str(catcher.exception),
- '[Errno %d] FALLOCATE_RESERVE fail 1024 <= 1024'
- % errno.ENOSPC)
- sys_fallocate_mock.assert_not_called()
-
- def test_not_enough_space_large(self, sys_fallocate_mock, fstatvfs_mock):
- # Want 1024 bytes in reserve plus 1GB allocated, and have 2 blocks
- # of size 1024 free, so fail
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('1024')
-
- fstatvfs_mock.return_value = self._statvfs_result(1024, 2)
- with self.assertRaises(OSError) as catcher:
- utils.fallocate(88, 1 << 30)
- self.assertEqual(
- str(catcher.exception),
- '[Errno %d] FALLOCATE_RESERVE fail %g <= 1024'
- % (errno.ENOSPC, ((2 * 1024) - (1 << 30))))
- sys_fallocate_mock.assert_not_called()
-
- def test_enough_space_small_blocks(self, sys_fallocate_mock,
- fstatvfs_mock):
- # Want 1024 bytes in reserve plus 1023 allocated, and have 4 blocks
- # of size 512 free, so succeed
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('1024')
-
- fstatvfs_mock.return_value = self._statvfs_result(512, 4)
- utils.fallocate(88, 1023)
-
- def test_not_enough_space_small_blocks(self, sys_fallocate_mock,
- fstatvfs_mock):
- # Want 1024 bytes in reserve plus 1024 allocated, and have 4 blocks
- # of size 512 free, so fail
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('1024')
-
- fstatvfs_mock.return_value = self._statvfs_result(512, 4)
- with self.assertRaises(OSError) as catcher:
- utils.fallocate(88, 1024)
- self.assertEqual(
- str(catcher.exception),
- '[Errno %d] FALLOCATE_RESERVE fail 1024 <= 1024'
- % errno.ENOSPC)
- sys_fallocate_mock.assert_not_called()
-
- def test_free_space_under_reserve(self, sys_fallocate_mock, fstatvfs_mock):
- # Want 2048 bytes in reserve but have only 3 blocks of size 512, so
- # allocating even 0 bytes fails
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('2048')
-
- fstatvfs_mock.return_value = self._statvfs_result(512, 3)
- with self.assertRaises(OSError) as catcher:
- utils.fallocate(88, 0)
- self.assertEqual(
- str(catcher.exception),
- '[Errno %d] FALLOCATE_RESERVE fail 1536 <= 2048'
- % errno.ENOSPC)
- sys_fallocate_mock.assert_not_called()
-
- def test_all_reserved(self, sys_fallocate_mock, fstatvfs_mock):
- # Filesystem is empty, but our reserve is bigger than the
- # filesystem, so any allocation will fail
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('9999999999999')
-
- fstatvfs_mock.return_value = self._statvfs_result(1024, 100)
- self.assertRaises(OSError, utils.fallocate, 88, 0)
- sys_fallocate_mock.assert_not_called()
-
- def test_enough_space_pct(self, sys_fallocate_mock, fstatvfs_mock):
- # Want 1% reserved, filesystem has 3/100 blocks of size 1024 free
- # and file size is 2047, so succeed
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('1%')
-
- fstatvfs_mock.return_value = self._statvfs_result(1024, 3)
- utils.fallocate(88, 2047)
-
- def test_not_enough_space_pct(self, sys_fallocate_mock, fstatvfs_mock):
- # Want 1% reserved, filesystem has 3/100 blocks of size 1024 free
- # and file size is 2048, so fail
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('1%')
-
- fstatvfs_mock.return_value = self._statvfs_result(1024, 3)
- with self.assertRaises(OSError) as catcher:
- utils.fallocate(88, 2048)
- self.assertEqual(
- str(catcher.exception),
- '[Errno %d] FALLOCATE_RESERVE fail 1 <= 1'
- % errno.ENOSPC)
- sys_fallocate_mock.assert_not_called()
-
- def test_all_space_reserved_pct(self, sys_fallocate_mock, fstatvfs_mock):
- # Filesystem is empty, but our reserve is the whole filesystem, so
- # any allocation will fail
- utils.FALLOCATE_RESERVE, utils.FALLOCATE_IS_PERCENT = \
- utils.config_fallocate_value('100%')
-
- fstatvfs_mock.return_value = self._statvfs_result(1024, 100)
- with self.assertRaises(OSError) as catcher:
- utils.fallocate(88, 0)
- self.assertEqual(
- str(catcher.exception),
- '[Errno %d] FALLOCATE_RESERVE fail 100 <= 100'
- % errno.ENOSPC)
- sys_fallocate_mock.assert_not_called()
-
-
-@patch('ctypes.get_errno')
-@patch.object(utils, '_sys_fallocate')
-class TestPunchHole(unittest.TestCase):
- def test_punch_hole(self, sys_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = True
- sys_fallocate_mock.return_value = 0
-
- utils.punch_hole(123, 456, 789)
-
- calls = sys_fallocate_mock.mock_calls
- self.assertEqual(len(calls), 1)
- args = calls[0][1]
- self.assertEqual(len(args), 4)
- self.assertEqual(args[0], 123)
- self.assertEqual(
- args[1], utils.FALLOC_FL_PUNCH_HOLE | utils.FALLOC_FL_KEEP_SIZE)
- self.assertEqual(args[2].value, 456)
- self.assertEqual(args[3].value, 789)
-
- def test_error(self, sys_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = True
- sys_fallocate_mock.return_value = -1
- get_errno_mock.return_value = errno.EISDIR
-
- with self.assertRaises(OSError) as cm:
- utils.punch_hole(123, 456, 789)
- self.assertEqual(cm.exception.errno, errno.EISDIR)
-
- def test_arg_bounds(self, sys_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = True
- sys_fallocate_mock.return_value = 0
-
- with self.assertRaises(ValueError):
- utils.punch_hole(0, 1, -1)
- with self.assertRaises(ValueError):
- utils.punch_hole(0, 1 << 64, 1)
- with self.assertRaises(ValueError):
- utils.punch_hole(0, -1, 1)
- with self.assertRaises(ValueError):
- utils.punch_hole(0, 1, 0)
- with self.assertRaises(ValueError):
- utils.punch_hole(0, 1, 1 << 64)
- self.assertEqual([], sys_fallocate_mock.mock_calls)
-
- # sanity check
- utils.punch_hole(0, 0, 1)
- self.assertEqual(
- [mock.call(
- 0, utils.FALLOC_FL_PUNCH_HOLE | utils.FALLOC_FL_KEEP_SIZE,
- mock.ANY, mock.ANY)],
- sys_fallocate_mock.mock_calls)
- # Go confirm the ctypes values separately; apparently == doesn't
- # work the way you'd expect with ctypes :-/
- self.assertEqual(sys_fallocate_mock.mock_calls[0][1][2].value, 0)
- self.assertEqual(sys_fallocate_mock.mock_calls[0][1][3].value, 1)
-
- def test_no_fallocate(self, sys_fallocate_mock, get_errno_mock):
- sys_fallocate_mock.available = False
-
- with self.assertRaises(OSError) as cm:
- utils.punch_hole(123, 456, 789)
- self.assertEqual(cm.exception.errno, errno.ENOTSUP)
-
-
-class TestPunchHoleReally(unittest.TestCase):
- def setUp(self):
- if not utils._sys_fallocate.available:
- raise unittest.SkipTest("utils._sys_fallocate not available")
-
- def test_punch_a_hole(self):
- with TemporaryFile() as tf:
- tf.write(b"x" * 64 + b"y" * 64 + b"z" * 64)
- tf.flush()
-
- # knock out the first half of the "y"s
- utils.punch_hole(tf.fileno(), 64, 32)
-
- tf.seek(0)
- contents = tf.read(4096)
- self.assertEqual(
- contents,
- b"x" * 64 + b"\0" * 32 + b"y" * 32 + b"z" * 64)
-
-
-class Test_LibcWrapper(unittest.TestCase):
- def test_available_function(self):
- # This should pretty much always exist
- getpid_wrapper = utils._LibcWrapper('getpid')
- self.assertTrue(getpid_wrapper.available)
- self.assertEqual(getpid_wrapper(), os.getpid())
-
- def test_unavailable_function(self):
- # This won't exist
- no_func_wrapper = utils._LibcWrapper('diffractively_protectorship')
- self.assertFalse(no_func_wrapper.available)
- self.assertRaises(NotImplementedError, no_func_wrapper)
-
- def test_argument_plumbing(self):
- lseek_wrapper = utils._LibcWrapper('lseek')
- with TemporaryFile() as tf:
- tf.write(b"abcdefgh")
- tf.flush()
- lseek_wrapper(tf.fileno(),
- ctypes.c_uint64(3),
- # 0 is SEEK_SET
- 0)
- self.assertEqual(tf.read(100), b"defgh")
-
-
class TestWatchdog(unittest.TestCase):
def test_start_stop(self):
w = utils.Watchdog()
diff --git a/test/unit/common/utils/test_libc.py b/test/unit/common/utils/test_libc.py
new file mode 100644
index 000000000..5357ce34d
--- /dev/null
+++ b/test/unit/common/utils/test_libc.py
@@ -0,0 +1,599 @@
+# Copyright (c) 2010-2023 OpenStack Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for swift.common.utils.libc"""
+
+import ctypes
+import errno
+import os
+import platform
+import posix
+import tempfile
+import unittest
+
+import mock
+
+from swift.common.utils import libc
+
+from test.debug_logger import debug_logger
+
+
+@mock.patch('ctypes.get_errno')
+@mock.patch.object(libc, '_sys_posix_fallocate')
+@mock.patch.object(libc, '_sys_fallocate')
+@mock.patch.object(libc, 'FALLOCATE_RESERVE', 0)
+class TestFallocate(unittest.TestCase):
+ def test_config_fallocate_value(self, sys_fallocate_mock,
+ sys_posix_fallocate_mock, get_errno_mock):
+ fallocate_value, is_percent = libc.config_fallocate_value('10%')
+ self.assertEqual(fallocate_value, 10)
+ self.assertTrue(is_percent)
+ fallocate_value, is_percent = libc.config_fallocate_value('10')
+ self.assertEqual(fallocate_value, 10)
+ self.assertFalse(is_percent)
+ try:
+ fallocate_value, is_percent = libc.config_fallocate_value('ab%')
+ except ValueError as err:
+ exc = err
+ self.assertEqual(str(exc), 'Error: ab% is an invalid value for '
+ 'fallocate_reserve.')
+ try:
+ fallocate_value, is_percent = libc.config_fallocate_value('ab')
+ except ValueError as err:
+ exc = err
+ self.assertEqual(str(exc), 'Error: ab is an invalid value for '
+ 'fallocate_reserve.')
+ try:
+ fallocate_value, is_percent = libc.config_fallocate_value('1%%')
+ except ValueError as err:
+ exc = err
+ self.assertEqual(str(exc), 'Error: 1%% is an invalid value for '
+ 'fallocate_reserve.')
+ try:
+ fallocate_value, is_percent = libc.config_fallocate_value('10.0')
+ except ValueError as err:
+ exc = err
+ self.assertEqual(str(exc), 'Error: 10.0 is an invalid value for '
+ 'fallocate_reserve.')
+ fallocate_value, is_percent = libc.config_fallocate_value('10.5%')
+ self.assertEqual(fallocate_value, 10.5)
+ self.assertTrue(is_percent)
+ fallocate_value, is_percent = libc.config_fallocate_value('10.000%')
+ self.assertEqual(fallocate_value, 10.000)
+ self.assertTrue(is_percent)
+
+ def test_fallocate(self, sys_fallocate_mock,
+ sys_posix_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = True
+ sys_fallocate_mock.return_value = 0
+
+ libc.fallocate(1234, 5000 * 2 ** 20)
+
+ # We can't use sys_fallocate_mock.assert_called_once_with because no
+ # two ctypes.c_uint64 objects are equal even if their values are
+ # equal. Yes, ctypes.c_uint64(123) != ctypes.c_uint64(123).
+ calls = sys_fallocate_mock.mock_calls
+ self.assertEqual(len(calls), 1)
+ args = calls[0][1]
+ self.assertEqual(len(args), 4)
+ self.assertEqual(args[0], 1234)
+ self.assertEqual(args[1], libc.FALLOC_FL_KEEP_SIZE)
+ self.assertEqual(args[2].value, 0)
+ self.assertEqual(args[3].value, 5000 * 2 ** 20)
+
+ sys_posix_fallocate_mock.assert_not_called()
+
+ def test_fallocate_offset(self, sys_fallocate_mock,
+ sys_posix_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = True
+ sys_fallocate_mock.return_value = 0
+
+ libc.fallocate(1234, 5000 * 2 ** 20, offset=3 * 2 ** 30)
+ calls = sys_fallocate_mock.mock_calls
+ self.assertEqual(len(calls), 1)
+ args = calls[0][1]
+ self.assertEqual(len(args), 4)
+ self.assertEqual(args[0], 1234)
+ self.assertEqual(args[1], libc.FALLOC_FL_KEEP_SIZE)
+ self.assertEqual(args[2].value, 3 * 2 ** 30)
+ self.assertEqual(args[3].value, 5000 * 2 ** 20)
+
+ sys_posix_fallocate_mock.assert_not_called()
+
+ def test_fallocate_fatal_error(self, sys_fallocate_mock,
+ sys_posix_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = True
+ sys_fallocate_mock.return_value = -1
+ get_errno_mock.return_value = errno.EIO
+
+ with self.assertRaises(OSError) as cm:
+ libc.fallocate(1234, 5000 * 2 ** 20)
+ self.assertEqual(cm.exception.errno, errno.EIO)
+
+ def test_fallocate_silent_errors(self, sys_fallocate_mock,
+ sys_posix_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = True
+ sys_fallocate_mock.return_value = -1
+
+ for silent_error in (0, errno.ENOSYS, errno.EOPNOTSUPP, errno.EINVAL):
+ get_errno_mock.return_value = silent_error
+ try:
+ libc.fallocate(1234, 5678)
+ except OSError:
+ self.fail("fallocate() raised an error on %d", silent_error)
+
+ def test_posix_fallocate_fallback(self, sys_fallocate_mock,
+ sys_posix_fallocate_mock,
+ get_errno_mock):
+ sys_fallocate_mock.available = False
+ sys_fallocate_mock.side_effect = NotImplementedError
+
+ sys_posix_fallocate_mock.available = True
+ sys_posix_fallocate_mock.return_value = 0
+
+ libc.fallocate(1234, 567890)
+ sys_fallocate_mock.assert_not_called()
+
+ calls = sys_posix_fallocate_mock.mock_calls
+ self.assertEqual(len(calls), 1)
+ args = calls[0][1]
+ self.assertEqual(len(args), 3)
+ self.assertEqual(args[0], 1234)
+ self.assertEqual(args[1].value, 0)
+ self.assertEqual(args[2].value, 567890)
+
+ def test_posix_fallocate_offset(self, sys_fallocate_mock,
+ sys_posix_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = False
+ sys_fallocate_mock.side_effect = NotImplementedError
+
+ sys_posix_fallocate_mock.available = True
+ sys_posix_fallocate_mock.return_value = 0
+
+ libc.fallocate(1234, 5000 * 2 ** 20, offset=3 * 2 ** 30)
+ calls = sys_posix_fallocate_mock.mock_calls
+ self.assertEqual(len(calls), 1)
+ args = calls[0][1]
+ self.assertEqual(len(args), 3)
+ self.assertEqual(args[0], 1234)
+ self.assertEqual(args[1].value, 3 * 2 ** 30)
+ self.assertEqual(args[2].value, 5000 * 2 ** 20)
+
+ sys_fallocate_mock.assert_not_called()
+
+ def test_no_fallocates_available(self, sys_fallocate_mock,
+ sys_posix_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = False
+ sys_posix_fallocate_mock.available = False
+
+ with mock.patch("logging.warning") as warning_mock, \
+ mock.patch.object(libc, "_fallocate_warned_about_missing",
+ False):
+ libc.fallocate(321, 654)
+ libc.fallocate(321, 654)
+
+ sys_fallocate_mock.assert_not_called()
+ sys_posix_fallocate_mock.assert_not_called()
+ get_errno_mock.assert_not_called()
+
+ self.assertEqual(len(warning_mock.mock_calls), 1)
+
+ def test_arg_bounds(self, sys_fallocate_mock,
+ sys_posix_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = True
+ sys_fallocate_mock.return_value = 0
+ with self.assertRaises(ValueError):
+ libc.fallocate(0, 1 << 64, 0)
+ with self.assertRaises(ValueError):
+ libc.fallocate(0, 0, -1)
+ with self.assertRaises(ValueError):
+ libc.fallocate(0, 0, 1 << 64)
+ self.assertEqual([], sys_fallocate_mock.mock_calls)
+ # sanity check
+ libc.fallocate(0, 0, 0)
+ self.assertEqual(
+ [mock.call(0, libc.FALLOC_FL_KEEP_SIZE, mock.ANY, mock.ANY)],
+ sys_fallocate_mock.mock_calls)
+ # Go confirm the ctypes values separately; apparently == doesn't
+ # work the way you'd expect with ctypes :-/
+ self.assertEqual(sys_fallocate_mock.mock_calls[0][1][2].value, 0)
+ self.assertEqual(sys_fallocate_mock.mock_calls[0][1][3].value, 0)
+ sys_fallocate_mock.reset_mock()
+
+ # negative size will be adjusted as 0
+ libc.fallocate(0, -1, 0)
+ self.assertEqual(
+ [mock.call(0, libc.FALLOC_FL_KEEP_SIZE, mock.ANY, mock.ANY)],
+ sys_fallocate_mock.mock_calls)
+ self.assertEqual(sys_fallocate_mock.mock_calls[0][1][2].value, 0)
+ self.assertEqual(sys_fallocate_mock.mock_calls[0][1][3].value, 0)
+
+
+@mock.patch.object(os, 'fstatvfs')
+@mock.patch.object(libc, '_sys_fallocate', available=True, return_value=0)
+@mock.patch.object(libc, 'FALLOCATE_RESERVE', 0)
+@mock.patch.object(libc, 'FALLOCATE_IS_PERCENT', False)
+@mock.patch.object(libc, '_fallocate_enabled', True)
+class TestFallocateReserve(unittest.TestCase):
+ def _statvfs_result(self, f_frsize, f_bavail):
+ # Only 3 values are relevant to us, so use zeros for the rest
+ f_blocks = 100
+ return posix.statvfs_result((0, f_frsize, f_blocks, 0, f_bavail,
+ 0, 0, 0, 0, 0))
+
+ def test_disabled(self, sys_fallocate_mock, fstatvfs_mock):
+ libc.disable_fallocate()
+ libc.fallocate(123, 456)
+
+ sys_fallocate_mock.assert_not_called()
+ fstatvfs_mock.assert_not_called()
+
+ def test_zero_reserve(self, sys_fallocate_mock, fstatvfs_mock):
+ libc.fallocate(123, 456)
+
+ fstatvfs_mock.assert_not_called()
+ self.assertEqual(len(sys_fallocate_mock.mock_calls), 1)
+
+ def test_enough_space(self, sys_fallocate_mock, fstatvfs_mock):
+ # Want 1024 bytes in reserve plus 1023 allocated, and have 2 blocks
+ # of size 1024 free, so succeed
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('1024')
+
+ fstatvfs_mock.return_value = self._statvfs_result(1024, 2)
+ libc.fallocate(88, 1023)
+
+ def test_not_enough_space(self, sys_fallocate_mock, fstatvfs_mock):
+ # Want 1024 bytes in reserve plus 1024 allocated, and have 2 blocks
+ # of size 1024 free, so fail
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('1024')
+
+ fstatvfs_mock.return_value = self._statvfs_result(1024, 2)
+ with self.assertRaises(OSError) as catcher:
+ libc.fallocate(88, 1024)
+ self.assertEqual(
+ str(catcher.exception),
+ '[Errno %d] FALLOCATE_RESERVE fail 1024 <= 1024'
+ % errno.ENOSPC)
+ sys_fallocate_mock.assert_not_called()
+
+ def test_not_enough_space_large(self, sys_fallocate_mock, fstatvfs_mock):
+ # Want 1024 bytes in reserve plus 1GB allocated, and have 2 blocks
+ # of size 1024 free, so fail
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('1024')
+
+ fstatvfs_mock.return_value = self._statvfs_result(1024, 2)
+ with self.assertRaises(OSError) as catcher:
+ libc.fallocate(88, 1 << 30)
+ self.assertEqual(
+ str(catcher.exception),
+ '[Errno %d] FALLOCATE_RESERVE fail %g <= 1024'
+ % (errno.ENOSPC, ((2 * 1024) - (1 << 30))))
+ sys_fallocate_mock.assert_not_called()
+
+ def test_enough_space_small_blocks(self, sys_fallocate_mock,
+ fstatvfs_mock):
+ # Want 1024 bytes in reserve plus 1023 allocated, and have 4 blocks
+ # of size 512 free, so succeed
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('1024')
+
+ fstatvfs_mock.return_value = self._statvfs_result(512, 4)
+ libc.fallocate(88, 1023)
+
+ def test_not_enough_space_small_blocks(self, sys_fallocate_mock,
+ fstatvfs_mock):
+ # Want 1024 bytes in reserve plus 1024 allocated, and have 4 blocks
+ # of size 512 free, so fail
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('1024')
+
+ fstatvfs_mock.return_value = self._statvfs_result(512, 4)
+ with self.assertRaises(OSError) as catcher:
+ libc.fallocate(88, 1024)
+ self.assertEqual(
+ str(catcher.exception),
+ '[Errno %d] FALLOCATE_RESERVE fail 1024 <= 1024'
+ % errno.ENOSPC)
+ sys_fallocate_mock.assert_not_called()
+
+ def test_free_space_under_reserve(self, sys_fallocate_mock, fstatvfs_mock):
+ # Want 2048 bytes in reserve but have only 3 blocks of size 512, so
+ # allocating even 0 bytes fails
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('2048')
+
+ fstatvfs_mock.return_value = self._statvfs_result(512, 3)
+ with self.assertRaises(OSError) as catcher:
+ libc.fallocate(88, 0)
+ self.assertEqual(
+ str(catcher.exception),
+ '[Errno %d] FALLOCATE_RESERVE fail 1536 <= 2048'
+ % errno.ENOSPC)
+ sys_fallocate_mock.assert_not_called()
+
+ def test_all_reserved(self, sys_fallocate_mock, fstatvfs_mock):
+ # Filesystem is empty, but our reserve is bigger than the
+ # filesystem, so any allocation will fail
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('9999999999999')
+
+ fstatvfs_mock.return_value = self._statvfs_result(1024, 100)
+ self.assertRaises(OSError, libc.fallocate, 88, 0)
+ sys_fallocate_mock.assert_not_called()
+
+ def test_enough_space_pct(self, sys_fallocate_mock, fstatvfs_mock):
+ # Want 1% reserved, filesystem has 3/100 blocks of size 1024 free
+ # and file size is 2047, so succeed
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('1%')
+
+ fstatvfs_mock.return_value = self._statvfs_result(1024, 3)
+ libc.fallocate(88, 2047)
+
+ def test_not_enough_space_pct(self, sys_fallocate_mock, fstatvfs_mock):
+ # Want 1% reserved, filesystem has 3/100 blocks of size 1024 free
+ # and file size is 2048, so fail
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('1%')
+
+ fstatvfs_mock.return_value = self._statvfs_result(1024, 3)
+ with self.assertRaises(OSError) as catcher:
+ libc.fallocate(88, 2048)
+ self.assertEqual(
+ str(catcher.exception),
+ '[Errno %d] FALLOCATE_RESERVE fail 1 <= 1'
+ % errno.ENOSPC)
+ sys_fallocate_mock.assert_not_called()
+
+ def test_all_space_reserved_pct(self, sys_fallocate_mock, fstatvfs_mock):
+ # Filesystem is empty, but our reserve is the whole filesystem, so
+ # any allocation will fail
+ libc.FALLOCATE_RESERVE, libc.FALLOCATE_IS_PERCENT = \
+ libc.config_fallocate_value('100%')
+
+ fstatvfs_mock.return_value = self._statvfs_result(1024, 100)
+ with self.assertRaises(OSError) as catcher:
+ libc.fallocate(88, 0)
+ self.assertEqual(
+ str(catcher.exception),
+ '[Errno %d] FALLOCATE_RESERVE fail 100 <= 100'
+ % errno.ENOSPC)
+ sys_fallocate_mock.assert_not_called()
+
+
+@mock.patch('ctypes.get_errno')
+@mock.patch.object(libc, '_sys_fallocate')
+class TestPunchHole(unittest.TestCase):
+ def test_punch_hole(self, sys_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = True
+ sys_fallocate_mock.return_value = 0
+
+ libc.punch_hole(123, 456, 789)
+
+ calls = sys_fallocate_mock.mock_calls
+ self.assertEqual(len(calls), 1)
+ args = calls[0][1]
+ self.assertEqual(len(args), 4)
+ self.assertEqual(args[0], 123)
+ self.assertEqual(
+ args[1], libc.FALLOC_FL_PUNCH_HOLE | libc.FALLOC_FL_KEEP_SIZE)
+ self.assertEqual(args[2].value, 456)
+ self.assertEqual(args[3].value, 789)
+
+ def test_error(self, sys_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = True
+ sys_fallocate_mock.return_value = -1
+ get_errno_mock.return_value = errno.EISDIR
+
+ with self.assertRaises(OSError) as cm:
+ libc.punch_hole(123, 456, 789)
+ self.assertEqual(cm.exception.errno, errno.EISDIR)
+
+ def test_arg_bounds(self, sys_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = True
+ sys_fallocate_mock.return_value = 0
+
+ with self.assertRaises(ValueError):
+ libc.punch_hole(0, 1, -1)
+ with self.assertRaises(ValueError):
+ libc.punch_hole(0, 1 << 64, 1)
+ with self.assertRaises(ValueError):
+ libc.punch_hole(0, -1, 1)
+ with self.assertRaises(ValueError):
+ libc.punch_hole(0, 1, 0)
+ with self.assertRaises(ValueError):
+ libc.punch_hole(0, 1, 1 << 64)
+ self.assertEqual([], sys_fallocate_mock.mock_calls)
+
+ # sanity check
+ libc.punch_hole(0, 0, 1)
+ self.assertEqual(
+ [mock.call(
+ 0, libc.FALLOC_FL_PUNCH_HOLE | libc.FALLOC_FL_KEEP_SIZE,
+ mock.ANY, mock.ANY)],
+ sys_fallocate_mock.mock_calls)
+ # Go confirm the ctypes values separately; apparently == doesn't
+ # work the way you'd expect with ctypes :-/
+ self.assertEqual(sys_fallocate_mock.mock_calls[0][1][2].value, 0)
+ self.assertEqual(sys_fallocate_mock.mock_calls[0][1][3].value, 1)
+
+ def test_no_fallocate(self, sys_fallocate_mock, get_errno_mock):
+ sys_fallocate_mock.available = False
+
+ with self.assertRaises(OSError) as cm:
+ libc.punch_hole(123, 456, 789)
+ self.assertEqual(cm.exception.errno, errno.ENOTSUP)
+
+
+class TestPunchHoleReally(unittest.TestCase):
+ def setUp(self):
+ if not libc._sys_fallocate.available:
+ raise unittest.SkipTest("libc._sys_fallocate not available")
+
+ def test_punch_a_hole(self):
+ with tempfile.TemporaryFile() as tf:
+ tf.write(b"x" * 64 + b"y" * 64 + b"z" * 64)
+ tf.flush()
+
+ # knock out the first half of the "y"s
+ libc.punch_hole(tf.fileno(), 64, 32)
+
+ tf.seek(0)
+ contents = tf.read(4096)
+ self.assertEqual(
+ contents,
+ b"x" * 64 + b"\0" * 32 + b"y" * 32 + b"z" * 64)
+
+
+class Test_LibcWrapper(unittest.TestCase):
+ def test_available_function(self):
+ # This should pretty much always exist
+ getpid_wrapper = libc._LibcWrapper('getpid')
+ self.assertTrue(getpid_wrapper.available)
+ self.assertEqual(getpid_wrapper(), os.getpid())
+
+ def test_unavailable_function(self):
+ # This won't exist
+ no_func_wrapper = libc._LibcWrapper('diffractively_protectorship')
+ self.assertFalse(no_func_wrapper.available)
+ self.assertRaises(NotImplementedError, no_func_wrapper)
+
+ def test_argument_plumbing(self):
+ lseek_wrapper = libc._LibcWrapper('lseek')
+ with tempfile.TemporaryFile() as tf:
+ tf.write(b"abcdefgh")
+ tf.flush()
+ lseek_wrapper(tf.fileno(),
+ ctypes.c_uint64(3),
+ # 0 is SEEK_SET
+ 0)
+ self.assertEqual(tf.read(100), b"defgh")
+
+
+class TestModifyPriority(unittest.TestCase):
+ def test_modify_priority(self):
+ pid = os.getpid()
+ logger = debug_logger()
+ called = {}
+
+ def _fake_setpriority(*args):
+ called['setpriority'] = args
+
+ def _fake_syscall(*args):
+ called['syscall'] = args
+
+ # Test if current architecture supports changing of priority
+ try:
+ libc.NR_ioprio_set()
+ except OSError as e:
+ raise unittest.SkipTest(e)
+
+ with mock.patch('swift.common.utils.libc._libc_setpriority',
+ _fake_setpriority), \
+ mock.patch('swift.common.utils.libc._posix_syscall',
+ _fake_syscall):
+ called = {}
+ # not set / default
+ libc.modify_priority({}, logger)
+ self.assertEqual(called, {})
+ called = {}
+ # just nice
+ libc.modify_priority({'nice_priority': '1'}, logger)
+ self.assertEqual(called, {'setpriority': (0, pid, 1)})
+ called = {}
+ # just ionice class uses default priority 0
+ libc.modify_priority({'ionice_class': 'IOPRIO_CLASS_RT'}, logger)
+ architecture = os.uname()[4]
+ arch_bits = platform.architecture()[0]
+ if architecture == 'x86_64' and arch_bits == '64bit':
+ self.assertEqual(called, {'syscall': (251, 1, pid, 1 << 13)})
+ elif architecture == 'aarch64' and arch_bits == '64bit':
+ self.assertEqual(called, {'syscall': (30, 1, pid, 1 << 13)})
+ else:
+ self.fail("Unexpected call: %r" % called)
+ called = {}
+ # just ionice priority is ignored
+ libc.modify_priority({'ionice_priority': '4'}, logger)
+ self.assertEqual(called, {})
+ called = {}
+ # bad ionice class
+ libc.modify_priority({'ionice_class': 'class_foo'}, logger)
+ self.assertEqual(called, {})
+ called = {}
+ # ionice class & priority
+ libc.modify_priority({
+ 'ionice_class': 'IOPRIO_CLASS_BE',
+ 'ionice_priority': '4',
+ }, logger)
+ if architecture == 'x86_64' and arch_bits == '64bit':
+ self.assertEqual(called, {
+ 'syscall': (251, 1, pid, 2 << 13 | 4)
+ })
+ elif architecture == 'aarch64' and arch_bits == '64bit':
+ self.assertEqual(called, {
+ 'syscall': (30, 1, pid, 2 << 13 | 4)
+ })
+ else:
+ self.fail("Unexpected call: %r" % called)
+ called = {}
+ # all
+ libc.modify_priority({
+ 'nice_priority': '-15',
+ 'ionice_class': 'IOPRIO_CLASS_IDLE',
+ 'ionice_priority': '6',
+ }, logger)
+ if architecture == 'x86_64' and arch_bits == '64bit':
+ self.assertEqual(called, {
+ 'setpriority': (0, pid, -15),
+ 'syscall': (251, 1, pid, 3 << 13 | 6),
+ })
+ elif architecture == 'aarch64' and arch_bits == '64bit':
+ self.assertEqual(called, {
+ 'setpriority': (0, pid, -15),
+ 'syscall': (30, 1, pid, 3 << 13 | 6),
+ })
+ else:
+ self.fail("Unexpected call: %r" % called)
+
+ def test__NR_ioprio_set(self):
+ with mock.patch('os.uname', return_value=('', '', '', '', 'x86_64')), \
+ mock.patch('platform.architecture',
+ return_value=('64bit', '')):
+ self.assertEqual(251, libc.NR_ioprio_set())
+
+ with mock.patch('os.uname', return_value=('', '', '', '', 'x86_64')), \
+ mock.patch('platform.architecture',
+ return_value=('32bit', '')):
+ self.assertRaises(OSError, libc.NR_ioprio_set)
+
+ with mock.patch('os.uname',
+ return_value=('', '', '', '', 'aarch64')), \
+ mock.patch('platform.architecture',
+ return_value=('64bit', '')):
+ self.assertEqual(30, libc.NR_ioprio_set())
+
+ with mock.patch('os.uname',
+ return_value=('', '', '', '', 'aarch64')), \
+ mock.patch('platform.architecture',
+ return_value=('32bit', '')):
+ self.assertRaises(OSError, libc.NR_ioprio_set)
+
+ with mock.patch('os.uname', return_value=('', '', '', '', 'alpha')), \
+ mock.patch('platform.architecture',
+ return_value=('64bit', '')):
+ self.assertRaises(OSError, libc.NR_ioprio_set)
diff --git a/test/unit/obj/test_diskfile.py b/test/unit/obj/test_diskfile.py
index 59baa38ab..8d3a484b7 100644
--- a/test/unit/obj/test_diskfile.py
+++ b/test/unit/obj/test_diskfile.py
@@ -47,6 +47,7 @@ from test.unit import (mock as unit_mock, temptree, mock_check_drive,
encode_frag_archive_bodies, skip_if_no_xattrs)
from swift.obj import diskfile
from swift.common import utils
+from swift.common.utils import libc
from swift.common.utils import hash_path, mkdirs, Timestamp, \
encode_timestamps, O_TMPFILE, md5 as _md5
from swift.common import ring
@@ -4748,7 +4749,7 @@ class DiskFileMixin(BaseDiskFileTestMixin):
# This is a horrible hack so you can run this test in isolation.
# Some of the ctypes machinery calls os.close(), and that runs afoul
# of our mock.
- with mock.patch.object(utils, '_sys_fallocate', None):
+ with mock.patch.object(libc, '_sys_fallocate', None):
utils.disable_fallocate()
df = self.df_mgr.get_diskfile(self.existing_device, '0', 'abc',