2020-01-27 06:01:40 +00:00
|
|
|
"""
|
|
|
|
Helper methods to test that MON and MDS caps are enforced properly.
|
|
|
|
"""
|
2022-04-28 07:31:42 +00:00
|
|
|
from os.path import join as os_path_join
|
2022-04-28 06:55:20 +00:00
|
|
|
from logging import getLogger
|
2022-04-28 07:31:42 +00:00
|
|
|
|
2020-01-27 06:01:40 +00:00
|
|
|
from tasks.cephfs.cephfs_test_case import CephFSTestCase
|
|
|
|
|
|
|
|
from teuthology.orchestra.run import Raw
|
|
|
|
|
2022-04-28 06:31:32 +00:00
|
|
|
|
2022-04-28 06:55:20 +00:00
|
|
|
log = getLogger(__name__)
|
|
|
|
|
|
|
|
|
2022-04-28 06:31:32 +00:00
|
|
|
class CapTester(CephFSTestCase):
|
|
|
|
"""
|
|
|
|
Test that MON and MDS caps are enforced.
|
|
|
|
|
|
|
|
MDS caps are tested by exercising read-write permissions and MON caps are
|
|
|
|
tested using output of command "ceph fs ls". Besides, it provides
|
|
|
|
write_test_files() which creates test files at the given path on CephFS
|
|
|
|
mounts passed to it.
|
|
|
|
|
|
|
|
USAGE: Call write_test_files() method at the beginning of the test and
|
|
|
|
once the caps that needs to be tested are assigned to the client and
|
|
|
|
CephFS be remount for caps to effective, call run_cap_tests(),
|
|
|
|
run_mon_cap_tests() or run_mds_cap_tests() as per the need.
|
|
|
|
"""
|
2020-01-27 06:01:40 +00:00
|
|
|
|
2022-04-28 03:51:03 +00:00
|
|
|
def write_test_files(self, mounts, testpath=''):
|
2022-04-28 07:31:42 +00:00
|
|
|
"""
|
|
|
|
Exercising 'r' and 'w' access levels on a file on CephFS mount is
|
|
|
|
pretty routine across all tests for caps. Adding to method to write
|
|
|
|
that file will reduce clutter in these tests.
|
|
|
|
|
|
|
|
This methods writes a fixed data in a file with a fixed name located
|
2022-04-28 06:31:32 +00:00
|
|
|
at the path passed in testpath for the given list of mounts. If
|
|
|
|
testpath is empty, the file is created at the root of the CephFS.
|
2022-04-28 07:31:42 +00:00
|
|
|
"""
|
2022-04-30 09:14:19 +00:00
|
|
|
dirname, filename = 'testdir', 'testfile'
|
2022-04-28 16:31:25 +00:00
|
|
|
self.test_set = []
|
2022-04-28 03:51:03 +00:00
|
|
|
# XXX: The reason behind testpath[1:] below is that the testpath is
|
|
|
|
# supposed to contain a path inside CephFS (which might be passed as
|
|
|
|
# an absolute path). os.path.join() deletes all previous path
|
|
|
|
# components when it encounters a path component starting with '/'.
|
|
|
|
# Deleting the first '/' from the string in testpath ensures that
|
|
|
|
# previous path components are not deleted by os.path.join().
|
|
|
|
if testpath:
|
|
|
|
testpath = testpath[1:] if testpath[0] == '/' else testpath
|
|
|
|
# XXX: passing just '/' screw up os.path.join() ahead.
|
|
|
|
if testpath == '/':
|
|
|
|
testpath = ''
|
2022-04-28 07:31:42 +00:00
|
|
|
|
2022-04-28 16:31:25 +00:00
|
|
|
for mount_x in mounts:
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info(f'creating test file on FS {mount_x.cephfs_name} '
|
|
|
|
f'mounted at {mount_x.mountpoint}...')
|
2022-04-28 03:51:03 +00:00
|
|
|
dirpath = os_path_join(mount_x.hostfs_mntpt, testpath, dirname)
|
2022-04-28 07:31:42 +00:00
|
|
|
mount_x.run_shell(f'mkdir {dirpath}')
|
|
|
|
filepath = os_path_join(dirpath, filename)
|
2022-04-30 09:14:19 +00:00
|
|
|
# XXX: the reason behind adding filepathm, cephfs_name and both
|
|
|
|
# mntpts is to avoid a test bug where we mount cephfs1 but what
|
|
|
|
# ends up being mounted cephfs2. since filepath and filedata are
|
|
|
|
# identical, how would tests figure otherwise that they are
|
|
|
|
# accessing the right filename but on wrong CephFS.
|
|
|
|
filedata = (f'filepath = {filepath}\n'
|
|
|
|
f'cephfs_name = {mount_x.cephfs_name}\n'
|
|
|
|
f'cephfs_mntpt = {mount_x.cephfs_mntpt}\n'
|
|
|
|
f'hostfs_mntpt = {mount_x.hostfs_mntpt}')
|
2022-04-28 16:31:25 +00:00
|
|
|
mount_x.write_file(filepath, filedata)
|
|
|
|
self.test_set.append((mount_x, filepath, filedata))
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info('test file created at {path} with data "{data}.')
|
2022-04-28 07:31:42 +00:00
|
|
|
|
2022-04-28 06:31:32 +00:00
|
|
|
def run_cap_tests(self, perm, mntpt=None):
|
2022-04-28 07:31:42 +00:00
|
|
|
# TODO
|
|
|
|
#self.run_mon_cap_tests()
|
2022-04-28 06:31:32 +00:00
|
|
|
self.run_mds_cap_tests(perm, mntpt=mntpt)
|
2022-04-28 07:31:42 +00:00
|
|
|
|
2022-04-30 07:35:53 +00:00
|
|
|
def _get_fsnames_from_moncap(self, moncap):
|
|
|
|
fsnames = []
|
|
|
|
while moncap.find('fsname=') != -1:
|
|
|
|
fsname_first_char = moncap.index('fsname=') + len('fsname=')
|
|
|
|
|
|
|
|
if ',' in moncap:
|
|
|
|
last = moncap.index(',')
|
|
|
|
fsname = moncap[fsname_first_char : last]
|
|
|
|
moncap = moncap.replace(moncap[0 : last+1], '')
|
|
|
|
else:
|
|
|
|
fsname = moncap[fsname_first_char : ]
|
|
|
|
moncap = moncap.replace(moncap[0 : ], '')
|
|
|
|
|
|
|
|
fsnames.append(fsname)
|
|
|
|
|
|
|
|
return fsnames
|
|
|
|
|
|
|
|
def run_mon_cap_tests(self, def_fs, client_id):
|
2022-04-28 06:31:32 +00:00
|
|
|
"""
|
|
|
|
Check that MON cap is enforced for a client by searching for a Ceph
|
|
|
|
FS name in output of cmd "fs ls" executed with that client's caps.
|
2022-04-30 07:35:53 +00:00
|
|
|
|
|
|
|
def_fs stands for default FS on Ceph cluster.
|
2022-04-28 06:31:32 +00:00
|
|
|
"""
|
2022-04-30 07:35:53 +00:00
|
|
|
get_cluster_cmd_op = def_fs.mon_manager.raw_cluster_cmd
|
|
|
|
|
|
|
|
keyring = get_cluster_cmd_op(args=f'auth get client.{client_id}')
|
|
|
|
|
|
|
|
moncap = None
|
|
|
|
for line in keyring.split('\n'):
|
|
|
|
if 'caps mon' in line:
|
|
|
|
moncap = line[line.find(' = "') + 4 : -1]
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise RuntimeError('run_mon_cap_tests(): mon cap not found in '
|
|
|
|
'keyring. keyring -\n' + keyring)
|
2020-01-27 06:01:40 +00:00
|
|
|
|
2022-04-30 07:35:53 +00:00
|
|
|
keyring_path = def_fs.admin_remote.mktemp(data=keyring)
|
|
|
|
|
|
|
|
fsls = get_cluster_cmd_op(
|
|
|
|
args=f'fs ls --id {client_id} -k {keyring_path}')
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info(f'output of fs ls cmd run by client.{client_id} -\n{fsls}')
|
2020-01-27 06:01:40 +00:00
|
|
|
|
2022-04-30 09:35:23 +00:00
|
|
|
if 'fsname=' not in moncap:
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info('no FS name is mentioned in moncap, client has '
|
|
|
|
'permission to list all files. moncap -\n{moncap}')
|
|
|
|
log.info('testing for presence of all FS names in output of '
|
|
|
|
'"fs ls" command run by client.')
|
|
|
|
|
2022-04-30 07:35:53 +00:00
|
|
|
fsls_admin = get_cluster_cmd_op(args='fs ls')
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info('output of fs ls cmd run by admin -\n{fsls_admin}')
|
|
|
|
|
2022-04-30 09:35:23 +00:00
|
|
|
self.assertEqual(fsls, fsls_admin)
|
2020-01-27 06:01:40 +00:00
|
|
|
return
|
|
|
|
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info('FS names are mentioned in moncap. moncap -\n{moncap}')
|
|
|
|
log.info('testing for presence of these FS names in output of '
|
|
|
|
'"fs ls" command run by client.')
|
2022-04-30 07:35:53 +00:00
|
|
|
for fsname in self._get_fsnames_from_moncap(moncap):
|
|
|
|
self.assertIn('name: ' + fsname, fsls)
|
2020-01-27 06:01:40 +00:00
|
|
|
|
2022-04-28 06:31:32 +00:00
|
|
|
def run_mds_cap_tests(self, perm, mntpt=None):
|
|
|
|
"""
|
|
|
|
Run test for read perm and, for write perm, run positive test if it
|
|
|
|
is present and run negative test if not.
|
|
|
|
"""
|
2022-04-28 03:51:03 +00:00
|
|
|
# XXX: mntpt is path inside cephfs that serves as root for current
|
2022-04-28 06:31:32 +00:00
|
|
|
# mount. Therefore, this path must me deleted from self.filepaths.
|
2022-04-28 03:51:03 +00:00
|
|
|
# Example -
|
|
|
|
# orignal path: /mnt/cephfs_x/dir1/dir2/testdir
|
|
|
|
# cephfs dir serving as root for current mnt: /dir1/dir2
|
|
|
|
# therefore, final path: /mnt/cephfs_x//testdir
|
|
|
|
if mntpt:
|
2022-04-28 16:31:25 +00:00
|
|
|
self.test_set = [(x, y.replace(mntpt, ''), z) for x, y, z in \
|
|
|
|
self.test_set]
|
2022-04-28 03:51:03 +00:00
|
|
|
|
2022-04-28 06:31:32 +00:00
|
|
|
self.conduct_pos_test_for_read_caps()
|
2020-01-27 06:01:40 +00:00
|
|
|
|
|
|
|
if perm == 'rw':
|
2022-04-28 06:31:32 +00:00
|
|
|
self.conduct_pos_test_for_write_caps()
|
2020-01-27 06:01:40 +00:00
|
|
|
elif perm == 'r':
|
2022-04-28 06:31:32 +00:00
|
|
|
self.conduct_neg_test_for_write_caps()
|
2020-01-27 06:01:40 +00:00
|
|
|
else:
|
|
|
|
raise RuntimeError(f'perm = {perm}\nIt should be "r" or "rw".')
|
|
|
|
|
2022-04-28 06:31:32 +00:00
|
|
|
def conduct_pos_test_for_read_caps(self):
|
2022-04-28 16:31:25 +00:00
|
|
|
for mount, path, data in self.test_set:
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info(f'test read perm: read file {path} and expect data '
|
|
|
|
f'"{data}"')
|
2022-04-28 16:31:25 +00:00
|
|
|
contents = mount.read_file(path)
|
|
|
|
self.assertEqual(data, contents)
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info(f'read perm was tested successfully: "{data}" was '
|
|
|
|
f'successfully read from path {path}')
|
2020-01-27 06:01:40 +00:00
|
|
|
|
2022-04-28 06:31:32 +00:00
|
|
|
def conduct_pos_test_for_write_caps(self):
|
2022-04-28 16:31:25 +00:00
|
|
|
for mount, path, data in self.test_set:
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info(f'test write perm: try writing data "{data}" to '
|
|
|
|
f'file {path}.')
|
2022-04-28 16:31:25 +00:00
|
|
|
mount.write_file(path=path, data=data)
|
|
|
|
contents = mount.read_file(path=path)
|
|
|
|
self.assertEqual(data, contents)
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info(f'write perm was tested was successfully: data '
|
|
|
|
f'"{data}" was successfully written to file "{path}".')
|
2020-01-27 06:01:40 +00:00
|
|
|
|
2022-06-09 15:09:40 +00:00
|
|
|
def conduct_neg_test_for_write_caps(self, sudo_write=False):
|
2021-09-15 09:11:02 +00:00
|
|
|
possible_errmsgs = ('permission denied', 'operation not permitted')
|
2022-06-09 15:09:40 +00:00
|
|
|
cmdargs = ['echo', 'some random data', Raw('|')]
|
|
|
|
cmdargs += ['sudo', 'tee'] if sudo_write else ['tee']
|
2020-01-27 06:01:40 +00:00
|
|
|
|
2022-04-28 16:31:25 +00:00
|
|
|
# don't use data, cmd args to write are set already above.
|
|
|
|
for mount, path, data in self.test_set:
|
2022-04-28 06:55:20 +00:00
|
|
|
log.info('test absence of write perm: expect failure '
|
|
|
|
f'writing data to file {path}.')
|
2022-04-28 16:31:25 +00:00
|
|
|
cmdargs.append(path)
|
|
|
|
mount.negtestcmd(args=cmdargs, retval=1, errmsgs=possible_errmsgs)
|
|
|
|
cmdargs.pop(-1)
|
2022-04-30 07:35:53 +00:00
|
|
|
log.info('absence of write perm was tested successfully: '
|
|
|
|
f'failed to be write data to file {path}.')
|