2 Before running this testsuite, add path to cephfs-shell module to $PATH and
9 from tempfile
import mkstemp
as tempfile_mkstemp
11 from six
import ensure_str
12 from sys
import version_info
as sys_version_info
13 from re
import search
as re_search
14 from time
import sleep
15 from tasks
.cephfs
.cephfs_test_case
import CephFSTestCase
16 from teuthology
.misc
import sudo_write_file
17 from teuthology
.orchestra
.run
import CommandFailedError
19 log
= logging
.getLogger(__name__
)
21 def humansize(nbytes
):
22 suffixes
= ['B', 'K', 'M', 'G', 'T', 'P']
24 while nbytes
>= 1024 and i
< len(suffixes
)-1:
27 nbytes
= math
.ceil(nbytes
)
28 f
= ('%d' % nbytes
).rstrip('.')
29 return '%s%s' % (f
, suffixes
[i
])
33 trueval
= ['true', 'yes', 'y', '1']
34 return True if val
== 1 or val
.lower() in trueval
else False
36 class TestCephFSShell(CephFSTestCase
):
39 def run_cephfs_shell_cmd(self
, cmd
, mount_x
=None, opts
=None, stdin
=None, config_path
=None):
41 mount_x
= self
.mount_a
42 if config_path
is None:
43 config_path
= self
.mount_a
.config_path
45 if isinstance(cmd
, list):
48 args
= ["cephfs-shell", "-c", config_path
]
53 args
.extend(("--", cmd
))
55 log
.info("Running command: {}".format(" ".join(args
)))
56 return mount_x
.client_remote
.run(args
=args
, stdout
=BytesIO(),
57 stderr
=BytesIO(), stdin
=stdin
)
59 def get_cephfs_shell_cmd_error(self
, cmd
, mount_x
=None, opts
=None,
61 return ensure_str(self
.run_cephfs_shell_cmd(cmd
, mount_x
, opts
, stdin
).stderr
.\
64 def get_cephfs_shell_cmd_output(self
, cmd
, mount_x
=None, opts
=None,
65 stdin
=None, config_path
=None):
66 return ensure_str(self
.run_cephfs_shell_cmd(cmd
, mount_x
, opts
, stdin
,
68 stdout
.getvalue().strip())
70 def get_cephfs_shell_script_output(self
, script
, mount_x
=None, stdin
=None):
71 return ensure_str(self
.run_cephfs_shell_script(script
, mount_x
, stdin
).stdout
.\
74 def run_cephfs_shell_script(self
, script
, mount_x
=None, stdin
=None):
76 mount_x
= self
.mount_a
78 scriptpath
= tempfile_mkstemp(prefix
='test-cephfs', text
=True)[1]
79 with
open(scriptpath
, 'w') as scriptfile
:
80 scriptfile
.write(script
)
81 # copy script to the machine running cephfs-shell.
82 mount_x
.client_remote
.put_file(scriptpath
, scriptpath
)
83 mount_x
.run_shell('chmod 755 ' + scriptpath
)
85 args
= ["cephfs-shell", "-c", mount_x
.config_path
, '-b', scriptpath
]
86 log
.info('Running script \"' + scriptpath
+ '\"')
87 return mount_x
.client_remote
.run(args
=args
, stdout
=BytesIO(),
88 stderr
=BytesIO(), stdin
=stdin
)
90 class TestMkdir(TestCephFSShell
):
93 Test that mkdir creates directory
95 o
= self
.get_cephfs_shell_cmd_output("mkdir d1")
96 log
.info("cephfs-shell output:\n{}".format(o
))
98 o
= self
.mount_a
.stat('d1')
99 log
.info("mount_a output:\n{}".format(o
))
101 def test_mkdir_with_07000_octal_mode(self
):
103 Test that mkdir fails with octal mode greater than 0777
105 o
= self
.get_cephfs_shell_cmd_output("mkdir -m 07000 d2")
106 log
.info("cephfs-shell output:\n{}".format(o
))
108 # mkdir d2 should fail
110 o
= self
.mount_a
.stat('d2')
111 log
.info("mount_a output:\n{}".format(o
))
115 def test_mkdir_with_negative_octal_mode(self
):
117 Test that mkdir fails with negative octal mode
119 o
= self
.get_cephfs_shell_cmd_output("mkdir -m -0755 d3")
120 log
.info("cephfs-shell output:\n{}".format(o
))
122 # mkdir d3 should fail
124 o
= self
.mount_a
.stat('d3')
125 log
.info("mount_a output:\n{}".format(o
))
129 def test_mkdir_with_non_octal_mode(self
):
131 Test that mkdir passes with non-octal mode
133 o
= self
.get_cephfs_shell_cmd_output("mkdir -m u=rwx d4")
134 log
.info("cephfs-shell output:\n{}".format(o
))
136 # mkdir d4 should pass
137 o
= self
.mount_a
.stat('d4')
138 assert((o
['st_mode'] & 0o700) == 0o700)
140 def test_mkdir_with_bad_non_octal_mode(self
):
142 Test that mkdir failes with bad non-octal mode
144 o
= self
.get_cephfs_shell_cmd_output("mkdir -m ugx=0755 d5")
145 log
.info("cephfs-shell output:\n{}".format(o
))
147 # mkdir d5 should fail
149 o
= self
.mount_a
.stat('d5')
150 log
.info("mount_a output:\n{}".format(o
))
154 def test_mkdir_path_without_path_option(self
):
156 Test that mkdir fails without path option for creating path
158 o
= self
.get_cephfs_shell_cmd_output("mkdir d5/d6/d7")
159 log
.info("cephfs-shell output:\n{}".format(o
))
161 # mkdir d5/d6/d7 should fail
163 o
= self
.mount_a
.stat('d5/d6/d7')
164 log
.info("mount_a output:\n{}".format(o
))
168 def test_mkdir_path_with_path_option(self
):
170 Test that mkdir passes with path option for creating path
172 o
= self
.get_cephfs_shell_cmd_output("mkdir -p d5/d6/d7")
173 log
.info("cephfs-shell output:\n{}".format(o
))
175 # mkdir d5/d6/d7 should pass
176 o
= self
.mount_a
.stat('d5/d6/d7')
177 log
.info("mount_a output:\n{}".format(o
))
179 class TestRmdir(TestCephFSShell
):
180 dir_name
= "test_dir"
182 def dir_does_not_exists(self
):
184 Tests that directory does not exists
187 self
.mount_a
.stat(self
.dir_name
)
188 except CommandFailedError
as e
:
189 if e
.exitstatus
== 2:
193 def test_rmdir(self
):
195 Test that rmdir deletes directory
197 self
.run_cephfs_shell_cmd("mkdir " + self
.dir_name
)
198 self
.run_cephfs_shell_cmd("rmdir "+ self
.dir_name
)
199 self
.dir_does_not_exists()
201 def test_rmdir_non_existing_dir(self
):
203 Test that rmdir does not delete a non existing directory
205 rmdir_output
= self
.get_cephfs_shell_cmd_error("rmdir test_dir")
206 log
.info("rmdir error output:\n{}".format(rmdir_output
))
207 self
.dir_does_not_exists()
209 def test_rmdir_dir_with_file(self
):
211 Test that rmdir does not delete directory containing file
213 self
.run_cephfs_shell_cmd("mkdir " + self
.dir_name
)
214 self
.run_cephfs_shell_cmd("put - test_dir/dumpfile", stdin
="Valid File")
215 self
.run_cephfs_shell_cmd("rmdir" + self
.dir_name
)
216 self
.mount_a
.stat(self
.dir_name
)
218 def test_rmdir_existing_file(self
):
220 Test that rmdir does not delete a file
222 self
.run_cephfs_shell_cmd("put - dumpfile", stdin
="Valid File")
223 self
.run_cephfs_shell_cmd("rmdir dumpfile")
224 self
.mount_a
.stat("dumpfile")
226 def test_rmdir_p(self
):
228 Test that rmdir -p deletes all empty directories in the root directory passed
230 self
.run_cephfs_shell_cmd("mkdir -p test_dir/t1/t2/t3")
231 self
.run_cephfs_shell_cmd("rmdir -p "+ self
.dir_name
)
232 self
.dir_does_not_exists()
234 def test_rmdir_p_valid_path(self
):
236 Test that rmdir -p deletes all empty directories in the path passed
238 self
.run_cephfs_shell_cmd("mkdir -p test_dir/t1/t2/t3")
239 self
.run_cephfs_shell_cmd("rmdir -p test_dir/t1/t2/t3")
240 self
.dir_does_not_exists()
242 def test_rmdir_p_non_existing_dir(self
):
244 Test that rmdir -p does not delete an invalid directory
246 rmdir_output
= self
.get_cephfs_shell_cmd_error("rmdir -p test_dir")
247 log
.info("rmdir error output:\n{}".format(rmdir_output
))
248 self
.dir_does_not_exists()
250 def test_rmdir_p_dir_with_file(self
):
252 Test that rmdir -p does not delete the directory containing a file
254 self
.run_cephfs_shell_cmd("mkdir " + self
.dir_name
)
255 self
.run_cephfs_shell_cmd("put - test_dir/dumpfile", stdin
="Valid File")
256 self
.run_cephfs_shell_cmd("rmdir -p " + self
.dir_name
)
257 self
.mount_a
.stat(self
.dir_name
)
259 class TestGetAndPut(TestCephFSShell
):
260 # the 'put' command gets tested as well with the 'get' comamnd
261 def test_put_and_get_without_target_directory(self
):
263 Test that put fails without target path
265 # generate test data in a directory
266 self
.run_cephfs_shell_cmd("!mkdir p1")
267 self
.run_cephfs_shell_cmd('!dd if=/dev/urandom of=p1/dump1 bs=1M count=1')
268 self
.run_cephfs_shell_cmd('!dd if=/dev/urandom of=p1/dump2 bs=2M count=1')
269 self
.run_cephfs_shell_cmd('!dd if=/dev/urandom of=p1/dump3 bs=3M count=1')
271 # copy the whole directory over to the cephfs
272 o
= self
.get_cephfs_shell_cmd_output("put p1")
273 log
.info("cephfs-shell output:\n{}".format(o
))
276 o
= self
.mount_a
.stat('p1')
277 log
.info("mount_a output:\n{}".format(o
))
278 o
= self
.mount_a
.stat('p1/dump1')
279 log
.info("mount_a output:\n{}".format(o
))
280 o
= self
.mount_a
.stat('p1/dump2')
281 log
.info("mount_a output:\n{}".format(o
))
282 o
= self
.mount_a
.stat('p1/dump3')
283 log
.info("mount_a output:\n{}".format(o
))
285 self
.run_cephfs_shell_cmd('!rm -rf p1')
286 o
= self
.get_cephfs_shell_cmd_output("get p1")
287 o
= self
.get_cephfs_shell_cmd_output('!stat p1 || echo $?')
288 log
.info("cephfs-shell output:\n{}".format(o
))
289 self
.validate_stat_output(o
)
291 o
= self
.get_cephfs_shell_cmd_output('!stat p1/dump1 || echo $?')
292 log
.info("cephfs-shell output:\n{}".format(o
))
293 self
.validate_stat_output(o
)
295 o
= self
.get_cephfs_shell_cmd_output('!stat p1/dump2 || echo $?')
296 log
.info("cephfs-shell output:\n{}".format(o
))
297 self
.validate_stat_output(o
)
299 o
= self
.get_cephfs_shell_cmd_output('!stat p1/dump3 || echo $?')
300 log
.info("cephfs-shell output:\n{}".format(o
))
301 self
.validate_stat_output(o
)
303 def validate_stat_output(self
, s
):
305 log
.info("lines:\n{}".format(l
))
306 rv
= l
[-1] # get last line; a failed stat will have "1" as the line
307 log
.info("rv:{}".format(rv
))
310 r
= int(rv
) # a non-numeric line will cause an exception
315 def test_get_with_target_name(self
):
317 Test that get passes with target name
320 s_hash
= crypt
.crypt(s
, '.A')
321 o
= self
.get_cephfs_shell_cmd_output("put - dump4", stdin
=s
)
322 log
.info("cephfs-shell output:\n{}".format(o
))
324 # put - dump4 should pass
325 o
= self
.mount_a
.stat('dump4')
326 log
.info("mount_a output:\n{}".format(o
))
328 o
= self
.get_cephfs_shell_cmd_output("get dump4 .")
329 log
.info("cephfs-shell output:\n{}".format(o
))
331 o
= self
.get_cephfs_shell_cmd_output("!cat dump4")
332 o_hash
= crypt
.crypt(o
, '.A')
334 # s_hash must be equal to o_hash
335 log
.info("s_hash:{}".format(s_hash
))
336 log
.info("o_hash:{}".format(o_hash
))
337 assert(s_hash
== o_hash
)
339 def test_get_without_target_name(self
):
341 Test that get passes with target name
344 o
= self
.get_cephfs_shell_cmd_output("put - dump5", stdin
=s
)
345 log
.info("cephfs-shell output:\n{}".format(o
))
347 # put - dump5 should pass
348 o
= self
.mount_a
.stat('dump5')
349 log
.info("mount_a output:\n{}".format(o
))
351 # get dump5 should fail
352 o
= self
.get_cephfs_shell_cmd_output("get dump5")
353 o
= self
.get_cephfs_shell_cmd_output("!stat dump5 || echo $?")
354 log
.info("cephfs-shell output:\n{}".format(o
))
358 # verify that stat dump5 passes
359 # if ret == 1, then that implies the stat failed
360 # which implies that there was a problem with "get dump5"
363 # we have a valid stat output; so this is good
364 # if the int() fails then that means there's a valid stat output
367 def test_get_to_console(self
):
369 Test that get passes with target name
372 s_hash
= crypt
.crypt(s
, '.A')
373 o
= self
.get_cephfs_shell_cmd_output("put - dump6", stdin
=s
)
374 log
.info("cephfs-shell output:\n{}".format(o
))
376 # put - dump6 should pass
377 o
= self
.mount_a
.stat('dump6')
378 log
.info("mount_a output:\n{}".format(o
))
380 # get dump6 - should pass
381 o
= self
.get_cephfs_shell_cmd_output("get dump6 -")
382 o_hash
= crypt
.crypt(o
, '.A')
383 log
.info("cephfs-shell output:\n{}".format(o
))
385 # s_hash must be equal to o_hash
386 log
.info("s_hash:{}".format(s_hash
))
387 log
.info("o_hash:{}".format(o_hash
))
388 assert(s_hash
== o_hash
)
390 class TestSnapshots(TestCephFSShell
):
393 Test that snapshot creation and deletion work
395 sd
= self
.fs
.get_config('client_snapdir')
396 sdn
= "data_dir/{}/snap1".format(sd
)
398 # create a data dir and dump some files into it
399 self
.get_cephfs_shell_cmd_output("mkdir data_dir")
401 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_a", stdin
=s
)
403 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_b", stdin
=s
)
405 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_c", stdin
=s
)
407 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_d", stdin
=s
)
409 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_e", stdin
=s
)
411 o
= self
.get_cephfs_shell_cmd_output("ls -l /data_dir")
412 log
.info("cephfs-shell output:\n{}".format(o
))
414 # create the snapshot - must pass
415 o
= self
.get_cephfs_shell_cmd_output("snap create snap1 /data_dir")
416 log
.info("cephfs-shell output:\n{}".format(o
))
417 self
.assertEqual("", o
)
418 o
= self
.mount_a
.stat(sdn
)
419 log
.info("mount_a output:\n{}".format(o
))
420 self
.assertIn('st_mode', o
)
422 # create the same snapshot again - must fail with an error message
423 o
= self
.get_cephfs_shell_cmd_error("snap create snap1 /data_dir")
424 log
.info("cephfs-shell output:\n{}".format(o
))
425 self
.assertIn("snapshot 'snap1' already exists", o
)
426 o
= self
.mount_a
.stat(sdn
)
427 log
.info("mount_a output:\n{}".format(o
))
428 self
.assertIn('st_mode', o
)
430 # delete the snapshot - must pass
431 o
= self
.get_cephfs_shell_cmd_output("snap delete snap1 /data_dir")
432 log
.info("cephfs-shell output:\n{}".format(o
))
433 self
.assertEqual("", o
)
435 o
= self
.mount_a
.stat(sdn
)
437 # snap dir should not exist anymore
439 log
.info("mount_a output:\n{}".format(o
))
440 self
.assertNotIn('st_mode', o
)
442 # delete the same snapshot again - must fail with an error message
443 o
= self
.get_cephfs_shell_cmd_error("snap delete snap1 /data_dir")
444 self
.assertIn("'snap1': no such snapshot", o
)
446 o
= self
.mount_a
.stat(sdn
)
449 log
.info("mount_a output:\n{}".format(o
))
450 self
.assertNotIn('st_mode', o
)
452 class TestCD(TestCephFSShell
):
455 def test_cd_with_no_args(self
):
457 Test that when cd is issued without any arguments, CWD is changed
460 path
= 'dir1/dir2/dir3'
461 self
.mount_a
.run_shell('mkdir -p ' + path
)
464 script
= 'cd {}\ncd\ncwd\n'.format(path
)
465 output
= self
.get_cephfs_shell_script_output(script
)
466 self
.assertEqual(output
, expected_cwd
)
468 def test_cd_with_args(self
):
470 Test that when cd is issued with an argument, CWD is changed
471 to the path passed in the argument.
473 path
= 'dir1/dir2/dir3'
474 self
.mount_a
.run_shell('mkdir -p ' + path
)
475 expected_cwd
= '/dir1/dir2/dir3'
477 script
= 'cd {}\ncwd\n'.format(path
)
478 output
= self
.get_cephfs_shell_script_output(script
)
479 self
.assertEqual(output
, expected_cwd
)
481 class TestDU(TestCephFSShell
):
484 def test_du_works_for_regfiles(self
):
485 regfilename
= 'some_regfile'
486 regfile_abspath
= path
.join(self
.mount_a
.mountpoint
, regfilename
)
487 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
, 'somedata')
489 size
= humansize(self
.mount_a
.stat(regfile_abspath
)['st_size'])
490 expected_output
= r
'{}{}{}'.format(size
, " +", regfilename
)
492 du_output
= self
.get_cephfs_shell_cmd_output('du ' + regfilename
)
493 if sys_version_info
.major
>= 3:
494 self
.assertRegex(du_output
, expected_output
)
495 elif sys_version_info
.major
< 3:
496 assert re_search(expected_output
, du_output
) != None, "\n" + \
497 "expected_output -\n{}\ndu_output -\n{}\n".format(
498 expected_output
, du_output
)
500 def test_du_works_for_non_empty_dirs(self
):
501 dirname
= 'some_directory'
502 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, dirname
)
503 regfilename
= 'some_regfile'
504 regfile_abspath
= path
.join(dir_abspath
, regfilename
)
505 self
.mount_a
.run_shell('mkdir ' + dir_abspath
)
506 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
, 'somedata')
508 # XXX: we stat `regfile_abspath` here because ceph du reports a non-empty
509 # directory's size as sum of sizes of all files under it.
510 size
= humansize(self
.mount_a
.stat(regfile_abspath
)['st_size'])
511 expected_output
= r
'{}{}{}'.format(size
, " +", dirname
)
514 du_output
= self
.get_cephfs_shell_cmd_output('du ' + dirname
)
515 if sys_version_info
.major
>= 3:
516 self
.assertRegex(du_output
, expected_output
)
517 elif sys_version_info
.major
< 3:
518 assert re_search(expected_output
, du_output
) != None, "\n" + \
519 "expected_output -\n{}\ndu_output -\n{}\n".format(
520 expected_output
, du_output
)
522 def test_du_works_for_empty_dirs(self
):
523 dirname
= 'some_directory'
524 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, dirname
)
525 self
.mount_a
.run_shell('mkdir ' + dir_abspath
)
527 size
= humansize(self
.mount_a
.stat(dir_abspath
)['st_size'])
528 expected_output
= r
'{}{}{}'.format(size
, " +", dirname
)
530 du_output
= self
.get_cephfs_shell_cmd_output('du ' + dirname
)
531 if sys_version_info
.major
>= 3:
532 self
.assertRegex(du_output
, expected_output
)
533 elif sys_version_info
.major
< 3:
534 assert re_search(expected_output
, du_output
) != None, "\n" + \
535 "expected_output -\n{}\ndu_output -\n{}\n".format(
536 expected_output
, du_output
)
538 def test_du_works_for_hardlinks(self
):
539 regfilename
= 'some_regfile'
540 regfile_abspath
= path
.join(self
.mount_a
.mountpoint
, regfilename
)
541 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
,
543 hlinkname
= 'some_hardlink'
544 hlink_abspath
= path
.join(self
.mount_a
.mountpoint
, hlinkname
)
545 self
.mount_a
.run_shell(['sudo', 'ln', regfile_abspath
,
546 hlink_abspath
], omit_sudo
=False)
548 size
= humansize(self
.mount_a
.stat(hlink_abspath
)['st_size'])
549 expected_output
= r
'{}{}{}'.format(size
, " +", hlinkname
)
551 du_output
= self
.get_cephfs_shell_cmd_output('du ' + hlinkname
)
552 if sys_version_info
.major
>= 3:
553 self
.assertRegex(du_output
, expected_output
)
554 elif sys_version_info
.major
< 3:
555 assert re_search(expected_output
, du_output
) != None, "\n" + \
556 "expected_output -\n{}\ndu_output -\n{}\n".format(
557 expected_output
, du_output
)
559 def test_du_works_for_softlinks_to_files(self
):
560 regfilename
= 'some_regfile'
561 regfile_abspath
= path
.join(self
.mount_a
.mountpoint
, regfilename
)
562 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
, 'somedata')
563 slinkname
= 'some_softlink'
564 slink_abspath
= path
.join(self
.mount_a
.mountpoint
, slinkname
)
565 self
.mount_a
.run_shell(['ln', '-s', regfile_abspath
, slink_abspath
])
567 size
= humansize(self
.mount_a
.lstat(slink_abspath
)['st_size'])
568 expected_output
= r
'{}{}{}'.format((size
), " +", slinkname
)
570 du_output
= self
.get_cephfs_shell_cmd_output('du ' + slinkname
)
571 if sys_version_info
.major
>= 3:
572 self
.assertRegex(du_output
, expected_output
)
573 elif sys_version_info
.major
< 3:
574 assert re_search(expected_output
, du_output
) != None, "\n" + \
575 "expected_output -\n{}\ndu_output -\n{}\n".format(
576 expected_output
, du_output
)
578 def test_du_works_for_softlinks_to_dirs(self
):
579 dirname
= 'some_directory'
580 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, dirname
)
581 self
.mount_a
.run_shell('mkdir ' + dir_abspath
)
582 slinkname
= 'some_softlink'
583 slink_abspath
= path
.join(self
.mount_a
.mountpoint
, slinkname
)
584 self
.mount_a
.run_shell(['ln', '-s', dir_abspath
, slink_abspath
])
586 size
= humansize(self
.mount_a
.lstat(slink_abspath
)['st_size'])
587 expected_output
= r
'{}{}{}'.format(size
, " +", slinkname
)
589 du_output
= self
.get_cephfs_shell_cmd_output('du ' + slinkname
)
590 if sys_version_info
.major
>= 3:
591 self
.assertRegex(du_output
, expected_output
)
592 elif sys_version_info
.major
< 3:
593 assert re_search(expected_output
, du_output
) != None, "\n" + \
594 "expected_output -\n{}\ndu_output -\n{}\n".format(
595 expected_output
, du_output
)
597 # NOTE: tests using these are pretty slow since to this methods sleeps for
599 def _setup_files(self
, return_path_to_files
=False, path_prefix
='./'):
601 regfilename
= 'regfile'
604 slink2name
= 'slink2'
606 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, dirname
)
607 regfile_abspath
= path
.join(self
.mount_a
.mountpoint
, regfilename
)
608 hlink_abspath
= path
.join(self
.mount_a
.mountpoint
, hlinkname
)
609 slink_abspath
= path
.join(self
.mount_a
.mountpoint
, slinkname
)
610 slink2_abspath
= path
.join(self
.mount_a
.mountpoint
, slink2name
)
612 self
.mount_a
.run_shell('mkdir ' + dir_abspath
)
613 self
.mount_a
.run_shell('touch ' + regfile_abspath
)
614 self
.mount_a
.run_shell(['ln', regfile_abspath
, hlink_abspath
])
615 self
.mount_a
.run_shell(['ln', '-s', regfile_abspath
, slink_abspath
])
616 self
.mount_a
.run_shell(['ln', '-s', dir_abspath
, slink2_abspath
])
620 regfile121_name
= 'regfile121'
621 dir2_abspath
= path
.join(self
.mount_a
.mountpoint
, dir2_name
)
622 dir21_abspath
= path
.join(dir2_abspath
, dir21_name
)
623 regfile121_abspath
= path
.join(dir21_abspath
, regfile121_name
)
624 self
.mount_a
.run_shell('mkdir -p ' + dir21_abspath
)
625 self
.mount_a
.run_shell('touch ' + regfile121_abspath
)
627 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
,
629 sudo_write_file(self
.mount_a
.client_remote
, regfile121_abspath
,
632 # TODO: is there a way to trigger/force update ceph.dir.rbytes?
633 # wait so that attr ceph.dir.rbytes gets a chance to be updated.
636 expected_patterns
= []
639 def append_expected_output_pattern(f
):
641 expected_patterns
.append(r
'{}{}{}'.format(size
, " +", '.' + f
))
643 expected_patterns
.append(r
'{}{}{}'.format(size
, " +",
644 path_prefix
+ path
.relpath(f
, self
.mount_a
.mountpoint
)))
646 for f
in [dir_abspath
, regfile_abspath
, regfile121_abspath
,
647 hlink_abspath
, slink_abspath
, slink2_abspath
]:
648 size
= humansize(self
.mount_a
.stat(f
, follow_symlinks
=
650 append_expected_output_pattern(f
)
652 # get size for directories containig regfiles within
653 for f
in [dir2_abspath
, dir21_abspath
]:
654 size
= humansize(self
.mount_a
.stat(regfile121_abspath
,
655 follow_symlinks
=False)['st_size'])
656 append_expected_output_pattern(f
)
658 # get size for CephFS root
660 for f
in [regfile_abspath
, regfile121_abspath
, slink_abspath
,
662 size
+= self
.mount_a
.stat(f
, follow_symlinks
=False)['st_size']
663 size
= humansize(size
)
664 append_expected_output_pattern('/')
666 if return_path_to_files
:
667 for p
in [dir_abspath
, regfile_abspath
, dir2_abspath
,
668 dir21_abspath
, regfile121_abspath
, hlink_abspath
,
669 slink_abspath
, slink2_abspath
]:
670 path_to_files
.append(path
.relpath(p
, self
.mount_a
.mountpoint
))
672 return (expected_patterns
, path_to_files
)
674 return expected_patterns
676 def test_du_works_recursively_with_no_path_in_args(self
):
677 expected_patterns_in_output
= self
._setup
_files
()
678 du_output
= self
.get_cephfs_shell_cmd_output('du -r')
680 for expected_output
in expected_patterns_in_output
:
681 if sys_version_info
.major
>= 3:
682 self
.assertRegex(du_output
, expected_output
)
683 elif sys_version_info
.major
< 3:
684 assert re_search(expected_output
, du_output
) != None, "\n" + \
685 "expected_output -\n{}\ndu_output -\n{}\n".format(
686 expected_output
, du_output
)
688 def test_du_with_path_in_args(self
):
689 expected_patterns_in_output
, path_to_files
= self
._setup
_files
(True,
693 for p
in path_to_files
:
695 du_output
= self
.get_cephfs_shell_cmd_output(args
)
697 for expected_output
in expected_patterns_in_output
:
698 if sys_version_info
.major
>= 3:
699 self
.assertRegex(du_output
, expected_output
)
700 elif sys_version_info
.major
< 3:
701 assert re_search(expected_output
, du_output
) != None, "\n" +\
702 "expected_output -\n{}\ndu_output -\n{}\n".format(
703 expected_output
, du_output
)
705 def test_du_with_no_args(self
):
706 expected_patterns_in_output
= self
._setup
_files
()
708 du_output
= self
.get_cephfs_shell_cmd_output('du')
710 for expected_output
in expected_patterns_in_output
:
711 # Since CWD is CephFS root and being non-recursive expect only
713 if expected_output
.find('/') == len(expected_output
) - 1:
714 if sys_version_info
.major
>= 3:
715 self
.assertRegex(du_output
, expected_output
)
716 elif sys_version_info
.major
< 3:
717 assert re_search(expected_output
, du_output
) != None, "\n" + \
718 "expected_output -\n{}\ndu_output -\n{}\n".format(
719 expected_output
, du_output
)
722 class TestDF(TestCephFSShell
):
723 def validate_df(self
, filename
):
724 df_output
= self
.get_cephfs_shell_cmd_output('df '+filename
)
725 log
.info("cephfs-shell df output:\n{}".format(df_output
))
727 shell_df
= df_output
.splitlines()[1].split()
729 block_size
= int(self
.mount_a
.df()["total"]) // 1024
730 log
.info("cephfs df block size output:{}\n".format(block_size
))
732 st_size
= int(self
.mount_a
.stat(filename
)["st_size"])
733 log
.info("cephfs stat used output:{}".format(st_size
))
734 log
.info("cephfs available:{}\n".format(block_size
- st_size
))
736 self
.assertTupleEqual((block_size
, st_size
, block_size
- st_size
),
737 (int(shell_df
[0]), int(shell_df
[1]) , int(shell_df
[2])))
739 def test_df_with_no_args(self
):
741 df_output
= self
.get_cephfs_shell_cmd_output('df')
742 assert df_output
== expected_output
744 def test_df_for_valid_directory(self
):
746 mount_output
= self
.mount_a
.run_shell('mkdir ' + dir_name
)
747 log
.info("cephfs-shell mount output:\n{}".format(mount_output
))
748 self
.validate_df(dir_name
)
750 def test_df_for_invalid_directory(self
):
751 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, 'non-existent-dir')
752 proc
= self
.run_cephfs_shell_cmd('df ' + dir_abspath
)
753 assert proc
.stderr
.getvalue().find(b
'error in stat') != -1
755 def test_df_for_valid_file(self
):
756 s
= 'df test' * 14145016
757 o
= self
.get_cephfs_shell_cmd_output("put - dumpfile", stdin
=s
)
758 log
.info("cephfs-shell output:\n{}".format(o
))
759 self
.validate_df("dumpfile")
762 class TestQuota(TestCephFSShell
):
765 def create_dir(self
):
766 mount_output
= self
.get_cephfs_shell_cmd_output('mkdir ' + self
.dir_name
)
767 log
.info("cephfs-shell mount output:\n{}".format(mount_output
))
769 def set_and_get_quota_vals(self
, input_val
):
770 quota_output
= self
.run_cephfs_shell_cmd('quota set --max_bytes '
771 + input_val
[0] + ' --max_files '
774 log
.info("cephfs-shell quota set output:\n{}".format(quota_output
))
776 quota_output
= self
.get_cephfs_shell_cmd_output('quota get '+ self
.dir_name
)
777 log
.info("cephfs-shell quota get output:\n{}".format(quota_output
))
779 quota_output
= quota_output
.split()
780 return quota_output
[1], quota_output
[3]
784 set_values
= ('6', '2')
785 self
.assertTupleEqual(self
.set_and_get_quota_vals(set_values
), set_values
)
787 def test_replace_values(self
):
789 set_values
= ('20', '4')
790 self
.assertTupleEqual(self
.set_and_get_quota_vals(set_values
), set_values
)
792 def test_set_invalid_dir(self
):
793 set_values
= ('5', '5')
795 self
.assertTupleEqual(self
.set_and_get_quota_vals(set_values
), set_values
)
796 raise Exception("Something went wrong!! Values set for non existing directory")
798 # Test should pass as values cannot be set for non existing directory
801 def test_set_invalid_values(self
):
803 set_values
= ('-6', '-5')
805 self
.assertTupleEqual(self
.set_and_get_quota_vals(set_values
), set_values
)
806 raise Exception("Something went wrong!! Invalid values set")
808 # Test should pass as invalid values cannot be set
811 def test_exceed_file_limit(self
):
813 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, self
.dir_name
)
814 self
.mount_a
.run_shell('touch '+dir_abspath
+'/file1')
815 file2
= path
.join(dir_abspath
, "file2")
817 self
.mount_a
.run_shell('touch '+file2
)
818 raise Exception("Something went wrong!! File creation should have failed")
819 except CommandFailedError
:
820 # Test should pass as file quota set to 2
821 # Additional condition to confirm file creation failure
822 if not path
.exists(file2
):
826 def test_exceed_write_limit(self
):
828 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, self
.dir_name
)
829 filename
= 'test_file'
830 file_abspath
= path
.join(dir_abspath
, filename
)
832 # Write should fail as bytes quota is set to 6
833 sudo_write_file(self
.mount_a
.client_remote
, file_abspath
,
834 'Disk raise Exception')
835 raise Exception("Write should have failed")
836 except CommandFailedError
:
837 # Test should pass only when write command fails
838 path_exists
= path
.exists(file_abspath
)
840 # Testing with teuthology: No file is created.
842 elif path_exists
and not path
.getsize(file_abspath
):
843 # Testing on Fedora 30: When write fails, empty file gets created.
849 class TestXattr(TestCephFSShell
):
852 def create_dir(self
):
853 self
.run_cephfs_shell_cmd('mkdir ' + self
.dir_name
)
855 def set_get_list_xattr_vals(self
, input_val
):
856 setxattr_output
= self
.get_cephfs_shell_cmd_output('setxattr '
860 + ' ' + input_val
[1])
861 log
.info("cephfs-shell setxattr output:\n{}".format(setxattr_output
))
863 getxattr_output
= self
.get_cephfs_shell_cmd_output('getxattr '
865 + ' ' + input_val
[0])
866 log
.info("cephfs-shell getxattr output:\n{}".format(getxattr_output
))
868 listxattr_output
= self
.get_cephfs_shell_cmd_output('listxattr '+ self
.dir_name
)
869 log
.info("cephfs-shell listxattr output:\n{}".format(listxattr_output
))
871 return listxattr_output
, getxattr_output
875 set_values
= ('user.key', '2')
876 self
.assertTupleEqual(self
.set_get_list_xattr_vals(set_values
), set_values
)
878 def test_reset(self
):
880 set_values
= ('user.key', '4')
881 self
.assertTupleEqual(self
.set_get_list_xattr_vals(set_values
), set_values
)
883 def test_non_existing_dir(self
):
884 set_values
= ('user.key', '9')
885 self
.assertTupleEqual(self
.set_get_list_xattr_vals(set_values
), (u
'', u
''))
889 # Test that ls passes
891 # o = self.get_cephfs_shell_cmd_output("ls")
892 # log.info("cephfs-shell output:\n{}".format(o))
894 # o = self.mount_a.run_shell(['ls']).stdout.getvalue().strip().replace("\n", " ").split()
895 # log.info("mount_a output:\n{}".format(o))
897 # # ls should not list hidden files without the -a switch
898 # if '.' in o or '..' in o:
899 # log.info('ls failed')
901 # log.info('ls succeeded')
903 # def test_ls_a(self):
905 # Test that ls -a passes
907 # o = self.get_cephfs_shell_cmd_output("ls -a")
908 # log.info("cephfs-shell output:\n{}".format(o))
910 # o = self.mount_a.run_shell(['ls', '-a']).stdout.getvalue().strip().replace("\n", " ").split()
911 # log.info("mount_a output:\n{}".format(o))
913 # if '.' in o and '..' in o:
914 # log.info('ls -a succeeded')
916 # log.info('ls -a failed')
918 class TestMisc(TestCephFSShell
):
919 def test_issue_cephfs_shell_cmd_at_invocation(self
):
921 Test that `cephfs-shell -c conf cmd` works.
923 # choosing a long name since short ones have a higher probability
924 # of getting matched by coincidence.
925 dirname
= 'somedirectory'
926 self
.run_cephfs_shell_cmd(['mkdir', dirname
])
928 output
= self
.mount_a
.client_remote
.sh([
929 'cephfs-shell', '-c', self
.mount_a
.config_path
, 'ls'
932 if sys_version_info
.major
>= 3:
933 self
.assertRegex(output
, dirname
)
934 elif sys_version_info
.major
< 3:
935 assert re_search(dirname
, output
) != None, "\n" + \
936 "expected_output -\n{}\ndu_output -\n{}\n".format(
941 Test that help outputs commands.
943 o
= self
.get_cephfs_shell_cmd_output("help all")
944 log
.info("output:\n{}".format(o
))
946 class TestConfReading(TestCephFSShell
):
947 def test_reading_conf_opt(self
):
949 Read conf without duplicate sections/options.
951 debugval
= self
.fs
.mon_manager
.raw_cluster_cmd('config', 'get',
952 'client','debug_shell')
953 debugval
= str_to_bool(debugval
)
954 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
955 'debug_shell', str(not debugval
))
956 output
= self
.get_cephfs_shell_cmd_output('set debug')
958 str_to_bool(output
[output
.find('debug: ') + len('debug: ') : ])
959 assert not debugval
== new_debug_val
961 def test_reading_conf_after_setting_opt_twice(self
):
963 Read conf without duplicate sections/options.
965 debugval
= self
.fs
.mon_manager
.raw_cluster_cmd('config', 'get',
966 'client','debug_shell')
967 debugval
= str_to_bool(debugval
)
969 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
970 'debug_shell', str(not debugval
))
971 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
972 'debug_shell', str(not debugval
))
973 output
= self
.get_cephfs_shell_cmd_output('set debug')
975 str_to_bool(output
[output
.find('debug: ') + len('debug: ') : ])
976 assert not debugval
== new_debug_val
978 def test_reading_conf_after_resetting_opt(self
):
979 debugval
= self
.fs
.mon_manager
.raw_cluster_cmd('config', 'get',
980 'client','debug_shell')
981 debugval
= str_to_bool(debugval
)
983 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
984 'debug_shell', str(not debugval
))
985 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'rm', 'client',
987 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
988 'debug_shell', str(not debugval
))
989 output
= self
.get_cephfs_shell_cmd_output('set debug')
991 str_to_bool(output
[output
.find('debug: ') + len('debug: ') : ])
992 assert not debugval
== new_debug_val