2 Before running this testsuite, add path to cephfs-shell module to $PATH and
8 from tempfile
import mkstemp
as tempfile_mkstemp
10 from sys
import version_info
as sys_version_info
11 from re
import search
as re_search
12 from time
import sleep
13 from StringIO
import StringIO
14 from tasks
.cephfs
.cephfs_test_case
import CephFSTestCase
15 from teuthology
.misc
import sudo_write_file
16 from teuthology
.orchestra
.run
import CommandFailedError
18 log
= logging
.getLogger(__name__
)
20 def humansize(nbytes
):
21 suffixes
= ['B', 'K', 'M', 'G', 'T', 'P']
23 while nbytes
>= 1024 and i
< len(suffixes
)-1:
26 nbytes
= math
.ceil(nbytes
)
27 f
= ('%d' % nbytes
).rstrip('.')
28 return '%s%s' % (f
, suffixes
[i
])
32 trueval
= ['true', 'yes', 'y', '1']
33 return True if val
== 1 or val
.lower() in trueval
else False
35 class TestCephFSShell(CephFSTestCase
):
38 def run_cephfs_shell_cmd(self
, cmd
, mount_x
=None, opts
=None, stdin
=None, config_path
=None):
40 mount_x
= self
.mount_a
41 if config_path
is None:
42 config_path
= self
.mount_a
.config_path
44 if isinstance(cmd
, list):
47 args
= ["cephfs-shell", "-c", config_path
]
52 args
.extend(("--", cmd
))
54 log
.info("Running command: {}".format(" ".join(args
)))
55 return mount_x
.client_remote
.run(args
=args
, stdout
=StringIO(),
56 stderr
=StringIO(), stdin
=stdin
)
58 def get_cephfs_shell_cmd_error(self
, cmd
, mount_x
=None, opts
=None,
60 return self
.run_cephfs_shell_cmd(cmd
, mount_x
, opts
, stdin
).stderr
.\
63 def get_cephfs_shell_cmd_output(self
, cmd
, mount_x
=None, opts
=None,
64 stdin
=None, config_path
=None):
65 return self
.run_cephfs_shell_cmd(cmd
, mount_x
, opts
, stdin
,
67 stdout
.getvalue().strip()
69 def get_cephfs_shell_script_output(self
, script
, mount_x
=None, stdin
=None):
70 return self
.run_cephfs_shell_script(script
, mount_x
, stdin
).stdout
.\
73 def run_cephfs_shell_script(self
, script
, mount_x
=None, stdin
=None):
75 mount_x
= self
.mount_a
77 scriptpath
= tempfile_mkstemp(prefix
='test-cephfs', text
=True)[1]
78 with
open(scriptpath
, 'w') as scriptfile
:
79 scriptfile
.write(script
)
80 # copy script to the machine running cephfs-shell.
81 mount_x
.client_remote
.put_file(scriptpath
, scriptpath
)
82 mount_x
.run_shell('chmod 755 ' + scriptpath
)
84 args
= ["cephfs-shell", "-c", mount_x
.config_path
, '-b', scriptpath
]
85 log
.info('Running script \"' + scriptpath
+ '\"')
86 return mount_x
.client_remote
.run(args
=args
, stdout
=StringIO(),
87 stderr
=StringIO(), stdin
=stdin
)
89 class TestMkdir(TestCephFSShell
):
92 Test that mkdir creates directory
94 o
= self
.get_cephfs_shell_cmd_output("mkdir d1")
95 log
.info("cephfs-shell output:\n{}".format(o
))
97 o
= self
.mount_a
.stat('d1')
98 log
.info("mount_a output:\n{}".format(o
))
100 def test_mkdir_with_07000_octal_mode(self
):
102 Test that mkdir fails with octal mode greater than 0777
104 o
= self
.get_cephfs_shell_cmd_output("mkdir -m 07000 d2")
105 log
.info("cephfs-shell output:\n{}".format(o
))
107 # mkdir d2 should fail
109 o
= self
.mount_a
.stat('d2')
110 log
.info("mount_a output:\n{}".format(o
))
114 def test_mkdir_with_negative_octal_mode(self
):
116 Test that mkdir fails with negative octal mode
118 o
= self
.get_cephfs_shell_cmd_output("mkdir -m -0755 d3")
119 log
.info("cephfs-shell output:\n{}".format(o
))
121 # mkdir d3 should fail
123 o
= self
.mount_a
.stat('d3')
124 log
.info("mount_a output:\n{}".format(o
))
128 def test_mkdir_with_non_octal_mode(self
):
130 Test that mkdir passes with non-octal mode
132 o
= self
.get_cephfs_shell_cmd_output("mkdir -m u=rwx d4")
133 log
.info("cephfs-shell output:\n{}".format(o
))
135 # mkdir d4 should pass
136 o
= self
.mount_a
.stat('d4')
137 assert((o
['st_mode'] & 0o700) == 0o700)
139 def test_mkdir_with_bad_non_octal_mode(self
):
141 Test that mkdir failes with bad non-octal mode
143 o
= self
.get_cephfs_shell_cmd_output("mkdir -m ugx=0755 d5")
144 log
.info("cephfs-shell output:\n{}".format(o
))
146 # mkdir d5 should fail
148 o
= self
.mount_a
.stat('d5')
149 log
.info("mount_a output:\n{}".format(o
))
153 def test_mkdir_path_without_path_option(self
):
155 Test that mkdir fails without path option for creating path
157 o
= self
.get_cephfs_shell_cmd_output("mkdir d5/d6/d7")
158 log
.info("cephfs-shell output:\n{}".format(o
))
160 # mkdir d5/d6/d7 should fail
162 o
= self
.mount_a
.stat('d5/d6/d7')
163 log
.info("mount_a output:\n{}".format(o
))
167 def test_mkdir_path_with_path_option(self
):
169 Test that mkdir passes with path option for creating path
171 o
= self
.get_cephfs_shell_cmd_output("mkdir -p d5/d6/d7")
172 log
.info("cephfs-shell output:\n{}".format(o
))
174 # mkdir d5/d6/d7 should pass
175 o
= self
.mount_a
.stat('d5/d6/d7')
176 log
.info("mount_a output:\n{}".format(o
))
178 class TestRmdir(TestCephFSShell
):
179 dir_name
= "test_dir"
181 def dir_does_not_exists(self
):
183 Tests that directory does not exists
186 self
.mount_a
.stat(self
.dir_name
)
187 except CommandFailedError
as e
:
188 if e
.exitstatus
== 2:
192 def test_rmdir(self
):
194 Test that rmdir deletes directory
196 self
.run_cephfs_shell_cmd("mkdir " + self
.dir_name
)
197 self
.run_cephfs_shell_cmd("rmdir "+ self
.dir_name
)
198 self
.dir_does_not_exists()
200 def test_rmdir_non_existing_dir(self
):
202 Test that rmdir does not delete a non existing directory
204 rmdir_output
= self
.get_cephfs_shell_cmd_error("rmdir test_dir")
205 log
.info("rmdir error output:\n{}".format(rmdir_output
))
206 self
.dir_does_not_exists()
208 def test_rmdir_dir_with_file(self
):
210 Test that rmdir does not delete directory containing file
212 self
.run_cephfs_shell_cmd("mkdir " + self
.dir_name
)
213 self
.run_cephfs_shell_cmd("put - test_dir/dumpfile", stdin
="Valid File")
214 self
.run_cephfs_shell_cmd("rmdir" + self
.dir_name
)
215 self
.mount_a
.stat(self
.dir_name
)
217 def test_rmdir_existing_file(self
):
219 Test that rmdir does not delete a file
221 self
.run_cephfs_shell_cmd("put - dumpfile", stdin
="Valid File")
222 self
.run_cephfs_shell_cmd("rmdir dumpfile")
223 self
.mount_a
.stat("dumpfile")
225 def test_rmdir_p(self
):
227 Test that rmdir -p deletes all empty directories in the root directory passed
229 self
.run_cephfs_shell_cmd("mkdir -p test_dir/t1/t2/t3")
230 self
.run_cephfs_shell_cmd("rmdir -p "+ self
.dir_name
)
231 self
.dir_does_not_exists()
233 def test_rmdir_p_valid_path(self
):
235 Test that rmdir -p deletes all empty directories in the path passed
237 self
.run_cephfs_shell_cmd("mkdir -p test_dir/t1/t2/t3")
238 self
.run_cephfs_shell_cmd("rmdir -p test_dir/t1/t2/t3")
239 self
.dir_does_not_exists()
241 def test_rmdir_p_non_existing_dir(self
):
243 Test that rmdir -p does not delete an invalid directory
245 rmdir_output
= self
.get_cephfs_shell_cmd_error("rmdir -p test_dir")
246 log
.info("rmdir error output:\n{}".format(rmdir_output
))
247 self
.dir_does_not_exists()
249 def test_rmdir_p_dir_with_file(self
):
251 Test that rmdir -p does not delete the directory containing a file
253 self
.run_cephfs_shell_cmd("mkdir " + self
.dir_name
)
254 self
.run_cephfs_shell_cmd("put - test_dir/dumpfile", stdin
="Valid File")
255 self
.run_cephfs_shell_cmd("rmdir -p " + self
.dir_name
)
256 self
.mount_a
.stat(self
.dir_name
)
258 class TestGetAndPut(TestCephFSShell
):
259 # the 'put' command gets tested as well with the 'get' comamnd
260 def test_put_and_get_without_target_directory(self
):
262 Test that put fails without target path
264 # generate test data in a directory
265 self
.run_cephfs_shell_cmd("!mkdir p1")
266 self
.run_cephfs_shell_cmd('!dd if=/dev/urandom of=p1/dump1 bs=1M count=1')
267 self
.run_cephfs_shell_cmd('!dd if=/dev/urandom of=p1/dump2 bs=2M count=1')
268 self
.run_cephfs_shell_cmd('!dd if=/dev/urandom of=p1/dump3 bs=3M count=1')
270 # copy the whole directory over to the cephfs
271 o
= self
.get_cephfs_shell_cmd_output("put p1")
272 log
.info("cephfs-shell output:\n{}".format(o
))
275 o
= self
.mount_a
.stat('p1')
276 log
.info("mount_a output:\n{}".format(o
))
277 o
= self
.mount_a
.stat('p1/dump1')
278 log
.info("mount_a output:\n{}".format(o
))
279 o
= self
.mount_a
.stat('p1/dump2')
280 log
.info("mount_a output:\n{}".format(o
))
281 o
= self
.mount_a
.stat('p1/dump3')
282 log
.info("mount_a output:\n{}".format(o
))
284 self
.run_cephfs_shell_cmd('!rm -rf p1')
285 o
= self
.get_cephfs_shell_cmd_output("get p1")
286 o
= self
.get_cephfs_shell_cmd_output('!stat p1 || echo $?')
287 log
.info("cephfs-shell output:\n{}".format(o
))
288 self
.validate_stat_output(o
)
290 o
= self
.get_cephfs_shell_cmd_output('!stat p1/dump1 || echo $?')
291 log
.info("cephfs-shell output:\n{}".format(o
))
292 self
.validate_stat_output(o
)
294 o
= self
.get_cephfs_shell_cmd_output('!stat p1/dump2 || echo $?')
295 log
.info("cephfs-shell output:\n{}".format(o
))
296 self
.validate_stat_output(o
)
298 o
= self
.get_cephfs_shell_cmd_output('!stat p1/dump3 || echo $?')
299 log
.info("cephfs-shell output:\n{}".format(o
))
300 self
.validate_stat_output(o
)
302 def validate_stat_output(self
, s
):
304 log
.info("lines:\n{}".format(l
))
305 rv
= l
[-1] # get last line; a failed stat will have "1" as the line
306 log
.info("rv:{}".format(rv
))
309 r
= int(rv
) # a non-numeric line will cause an exception
314 def test_get_with_target_name(self
):
316 Test that get passes with target name
319 s_hash
= crypt
.crypt(s
, '.A')
320 o
= self
.get_cephfs_shell_cmd_output("put - dump4", stdin
=s
)
321 log
.info("cephfs-shell output:\n{}".format(o
))
323 # put - dump4 should pass
324 o
= self
.mount_a
.stat('dump4')
325 log
.info("mount_a output:\n{}".format(o
))
327 o
= self
.get_cephfs_shell_cmd_output("get dump4 .")
328 log
.info("cephfs-shell output:\n{}".format(o
))
330 o
= self
.get_cephfs_shell_cmd_output("!cat dump4")
331 o_hash
= crypt
.crypt(o
, '.A')
333 # s_hash must be equal to o_hash
334 log
.info("s_hash:{}".format(s_hash
))
335 log
.info("o_hash:{}".format(o_hash
))
336 assert(s_hash
== o_hash
)
338 def test_get_without_target_name(self
):
340 Test that get passes with target name
343 o
= self
.get_cephfs_shell_cmd_output("put - dump5", stdin
=s
)
344 log
.info("cephfs-shell output:\n{}".format(o
))
346 # put - dump5 should pass
347 o
= self
.mount_a
.stat('dump5')
348 log
.info("mount_a output:\n{}".format(o
))
350 # get dump5 should fail
351 o
= self
.get_cephfs_shell_cmd_output("get dump5")
352 o
= self
.get_cephfs_shell_cmd_output("!stat dump5 || echo $?")
353 log
.info("cephfs-shell output:\n{}".format(o
))
357 # verify that stat dump5 passes
358 # if ret == 1, then that implies the stat failed
359 # which implies that there was a problem with "get dump5"
362 # we have a valid stat output; so this is good
363 # if the int() fails then that means there's a valid stat output
366 def test_get_to_console(self
):
368 Test that get passes with target name
371 s_hash
= crypt
.crypt(s
, '.A')
372 o
= self
.get_cephfs_shell_cmd_output("put - dump6", stdin
=s
)
373 log
.info("cephfs-shell output:\n{}".format(o
))
375 # put - dump6 should pass
376 o
= self
.mount_a
.stat('dump6')
377 log
.info("mount_a output:\n{}".format(o
))
379 # get dump6 - should pass
380 o
= self
.get_cephfs_shell_cmd_output("get dump6 -")
381 o_hash
= crypt
.crypt(o
, '.A')
382 log
.info("cephfs-shell output:\n{}".format(o
))
384 # s_hash must be equal to o_hash
385 log
.info("s_hash:{}".format(s_hash
))
386 log
.info("o_hash:{}".format(o_hash
))
387 assert(s_hash
== o_hash
)
389 class TestSnapshots(TestCephFSShell
):
392 Test that snapshot creation and deletion work
394 sd
= self
.fs
.get_config('client_snapdir')
395 sdn
= "data_dir/{}/snap1".format(sd
)
397 # create a data dir and dump some files into it
398 self
.get_cephfs_shell_cmd_output("mkdir data_dir")
400 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_a", stdin
=s
)
402 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_b", stdin
=s
)
404 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_c", stdin
=s
)
406 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_d", stdin
=s
)
408 o
= self
.get_cephfs_shell_cmd_output("put - data_dir/data_e", stdin
=s
)
410 o
= self
.get_cephfs_shell_cmd_output("ls -l /data_dir")
411 log
.info("cephfs-shell output:\n{}".format(o
))
413 # create the snapshot - must pass
414 o
= self
.get_cephfs_shell_cmd_output("snap create snap1 /data_dir")
415 log
.info("cephfs-shell output:\n{}".format(o
))
416 self
.assertEqual("", o
)
417 o
= self
.mount_a
.stat(sdn
)
418 log
.info("mount_a output:\n{}".format(o
))
419 self
.assertIn('st_mode', o
)
421 # create the same snapshot again - must fail with an error message
422 o
= self
.get_cephfs_shell_cmd_error("snap create snap1 /data_dir")
423 log
.info("cephfs-shell output:\n{}".format(o
))
424 self
.assertIn("snapshot 'snap1' already exists", o
)
425 o
= self
.mount_a
.stat(sdn
)
426 log
.info("mount_a output:\n{}".format(o
))
427 self
.assertIn('st_mode', o
)
429 # delete the snapshot - must pass
430 o
= self
.get_cephfs_shell_cmd_output("snap delete snap1 /data_dir")
431 log
.info("cephfs-shell output:\n{}".format(o
))
432 self
.assertEqual("", o
)
434 o
= self
.mount_a
.stat(sdn
)
436 # snap dir should not exist anymore
438 log
.info("mount_a output:\n{}".format(o
))
439 self
.assertNotIn('st_mode', o
)
441 # delete the same snapshot again - must fail with an error message
442 o
= self
.get_cephfs_shell_cmd_error("snap delete snap1 /data_dir")
443 self
.assertIn("'snap1': no such snapshot", o
)
445 o
= self
.mount_a
.stat(sdn
)
448 log
.info("mount_a output:\n{}".format(o
))
449 self
.assertNotIn('st_mode', o
)
451 class TestCD(TestCephFSShell
):
454 def test_cd_with_no_args(self
):
456 Test that when cd is issued without any arguments, CWD is changed
459 path
= 'dir1/dir2/dir3'
460 self
.mount_a
.run_shell('mkdir -p ' + path
)
463 script
= 'cd {}\ncd\ncwd\n'.format(path
)
464 output
= self
.get_cephfs_shell_script_output(script
)
465 self
.assertEqual(output
, expected_cwd
)
467 def test_cd_with_args(self
):
469 Test that when cd is issued with an argument, CWD is changed
470 to the path passed in the argument.
472 path
= 'dir1/dir2/dir3'
473 self
.mount_a
.run_shell('mkdir -p ' + path
)
474 expected_cwd
= '/dir1/dir2/dir3'
476 script
= 'cd {}\ncwd\n'.format(path
)
477 output
= self
.get_cephfs_shell_script_output(script
)
478 self
.assertEqual(output
, expected_cwd
)
480 class TestDU(TestCephFSShell
):
483 def test_du_works_for_regfiles(self
):
484 regfilename
= 'some_regfile'
485 regfile_abspath
= path
.join(self
.mount_a
.mountpoint
, regfilename
)
486 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
, 'somedata')
488 size
= humansize(self
.mount_a
.stat(regfile_abspath
)['st_size'])
489 expected_output
= r
'{}{}{}'.format(size
, " +", regfilename
)
491 du_output
= self
.get_cephfs_shell_cmd_output('du ' + regfilename
)
492 if sys_version_info
.major
>= 3:
493 self
.assertRegex(expected_output
, du_output
)
494 elif sys_version_info
.major
< 3:
495 assert re_search(expected_output
, du_output
) != None, "\n" + \
496 "expected_output -\n{}\ndu_output -\n{}\n".format(
497 expected_output
, du_output
)
499 def test_du_works_for_non_empty_dirs(self
):
500 dirname
= 'some_directory'
501 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, dirname
)
502 regfilename
= 'some_regfile'
503 regfile_abspath
= path
.join(dir_abspath
, regfilename
)
504 self
.mount_a
.run_shell('mkdir ' + dir_abspath
)
505 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
, 'somedata')
507 # XXX: we stat `regfile_abspath` here because ceph du reports a non-empty
508 # directory's size as sum of sizes of all files under it.
509 size
= humansize(self
.mount_a
.stat(regfile_abspath
)['st_size'])
510 expected_output
= r
'{}{}{}'.format(size
, " +", dirname
)
513 du_output
= self
.get_cephfs_shell_cmd_output('du ' + dirname
)
514 if sys_version_info
.major
>= 3:
515 self
.assertRegex(expected_output
, du_output
)
516 elif sys_version_info
.major
< 3:
517 assert re_search(expected_output
, du_output
) != None, "\n" + \
518 "expected_output -\n{}\ndu_output -\n{}\n".format(
519 expected_output
, du_output
)
521 def test_du_works_for_empty_dirs(self
):
522 dirname
= 'some_directory'
523 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, dirname
)
524 self
.mount_a
.run_shell('mkdir ' + dir_abspath
)
526 size
= humansize(self
.mount_a
.stat(dir_abspath
)['st_size'])
527 expected_output
= r
'{}{}{}'.format(size
, " +", dirname
)
529 du_output
= self
.get_cephfs_shell_cmd_output('du ' + dirname
)
530 if sys_version_info
.major
>= 3:
531 self
.assertRegex(expected_output
, du_output
)
532 elif sys_version_info
.major
< 3:
533 assert re_search(expected_output
, du_output
) != None, "\n" + \
534 "expected_output -\n{}\ndu_output -\n{}\n".format(
535 expected_output
, du_output
)
537 def test_du_works_for_hardlinks(self
):
538 regfilename
= 'some_regfile'
539 regfile_abspath
= path
.join(self
.mount_a
.mountpoint
, regfilename
)
540 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
,
542 hlinkname
= 'some_hardlink'
543 hlink_abspath
= path
.join(self
.mount_a
.mountpoint
, hlinkname
)
544 self
.mount_a
.run_shell(['sudo', 'ln', regfile_abspath
,
545 hlink_abspath
], omit_sudo
=False)
547 size
= humansize(self
.mount_a
.stat(hlink_abspath
)['st_size'])
548 expected_output
= r
'{}{}{}'.format(size
, " +", hlinkname
)
550 du_output
= self
.get_cephfs_shell_cmd_output('du ' + hlinkname
)
551 if sys_version_info
.major
>= 3:
552 self
.assertRegex(expected_output
, du_output
)
553 elif sys_version_info
.major
< 3:
554 assert re_search(expected_output
, du_output
) != None, "\n" + \
555 "expected_output -\n{}\ndu_output -\n{}\n".format(
556 expected_output
, du_output
)
558 def test_du_works_for_softlinks_to_files(self
):
559 regfilename
= 'some_regfile'
560 regfile_abspath
= path
.join(self
.mount_a
.mountpoint
, regfilename
)
561 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
, 'somedata')
562 slinkname
= 'some_softlink'
563 slink_abspath
= path
.join(self
.mount_a
.mountpoint
, slinkname
)
564 self
.mount_a
.run_shell(['ln', '-s', regfile_abspath
, slink_abspath
])
566 size
= humansize(self
.mount_a
.lstat(slink_abspath
)['st_size'])
567 expected_output
= r
'{}{}{}'.format((size
), " +", slinkname
)
569 du_output
= self
.get_cephfs_shell_cmd_output('du ' + slinkname
)
570 if sys_version_info
.major
>= 3:
571 self
.assertRegex(expected_output
, du_output
)
572 elif sys_version_info
.major
< 3:
573 assert re_search(expected_output
, du_output
) != None, "\n" + \
574 "expected_output -\n{}\ndu_output -\n{}\n".format(
575 expected_output
, du_output
)
577 def test_du_works_for_softlinks_to_dirs(self
):
578 dirname
= 'some_directory'
579 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, dirname
)
580 self
.mount_a
.run_shell('mkdir ' + dir_abspath
)
581 slinkname
= 'some_softlink'
582 slink_abspath
= path
.join(self
.mount_a
.mountpoint
, slinkname
)
583 self
.mount_a
.run_shell(['ln', '-s', dir_abspath
, slink_abspath
])
585 size
= humansize(self
.mount_a
.lstat(slink_abspath
)['st_size'])
586 expected_output
= r
'{}{}{}'.format(size
, " +", slinkname
)
588 du_output
= self
.get_cephfs_shell_cmd_output('du ' + slinkname
)
589 if sys_version_info
.major
>= 3:
590 self
.assertRegex(expected_output
, du_output
)
591 elif sys_version_info
.major
< 3:
592 assert re_search(expected_output
, du_output
) != None, "\n" + \
593 "expected_output -\n{}\ndu_output -\n{}\n".format(
594 expected_output
, du_output
)
596 # NOTE: tests using these are pretty slow since to this methods sleeps for
598 def _setup_files(self
, return_path_to_files
=False, path_prefix
='./'):
600 regfilename
= 'regfile'
603 slink2name
= 'slink2'
605 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, dirname
)
606 regfile_abspath
= path
.join(self
.mount_a
.mountpoint
, regfilename
)
607 hlink_abspath
= path
.join(self
.mount_a
.mountpoint
, hlinkname
)
608 slink_abspath
= path
.join(self
.mount_a
.mountpoint
, slinkname
)
609 slink2_abspath
= path
.join(self
.mount_a
.mountpoint
, slink2name
)
611 self
.mount_a
.run_shell('mkdir ' + dir_abspath
)
612 self
.mount_a
.run_shell('touch ' + regfile_abspath
)
613 self
.mount_a
.run_shell(['ln', regfile_abspath
, hlink_abspath
])
614 self
.mount_a
.run_shell(['ln', '-s', regfile_abspath
, slink_abspath
])
615 self
.mount_a
.run_shell(['ln', '-s', dir_abspath
, slink2_abspath
])
619 regfile121_name
= 'regfile121'
620 dir2_abspath
= path
.join(self
.mount_a
.mountpoint
, dir2_name
)
621 dir21_abspath
= path
.join(dir2_abspath
, dir21_name
)
622 regfile121_abspath
= path
.join(dir21_abspath
, regfile121_name
)
623 self
.mount_a
.run_shell('mkdir -p ' + dir21_abspath
)
624 self
.mount_a
.run_shell('touch ' + regfile121_abspath
)
626 sudo_write_file(self
.mount_a
.client_remote
, regfile_abspath
,
628 sudo_write_file(self
.mount_a
.client_remote
, regfile121_abspath
,
631 # TODO: is there a way to trigger/force update ceph.dir.rbytes?
632 # wait so that attr ceph.dir.rbytes gets a chance to be updated.
635 expected_patterns
= []
638 def append_expected_output_pattern(f
):
640 expected_patterns
.append(r
'{}{}{}'.format(size
, " +", '.' + f
))
642 expected_patterns
.append(r
'{}{}{}'.format(size
, " +",
643 path_prefix
+ path
.relpath(f
, self
.mount_a
.mountpoint
)))
645 for f
in [dir_abspath
, regfile_abspath
, regfile121_abspath
,
646 hlink_abspath
, slink_abspath
, slink2_abspath
]:
647 size
= humansize(self
.mount_a
.stat(f
, follow_symlinks
=
649 append_expected_output_pattern(f
)
651 # get size for directories containig regfiles within
652 for f
in [dir2_abspath
, dir21_abspath
]:
653 size
= humansize(self
.mount_a
.stat(regfile121_abspath
,
654 follow_symlinks
=False)['st_size'])
655 append_expected_output_pattern(f
)
657 # get size for CephFS root
659 for f
in [regfile_abspath
, regfile121_abspath
, slink_abspath
,
661 size
+= self
.mount_a
.stat(f
, follow_symlinks
=False)['st_size']
662 size
= humansize(size
)
663 append_expected_output_pattern('/')
665 if return_path_to_files
:
666 for p
in [dir_abspath
, regfile_abspath
, dir2_abspath
,
667 dir21_abspath
, regfile121_abspath
, hlink_abspath
,
668 slink_abspath
, slink2_abspath
]:
669 path_to_files
.append(path
.relpath(p
, self
.mount_a
.mountpoint
))
671 return (expected_patterns
, path_to_files
)
673 return expected_patterns
675 def test_du_works_recursively_with_no_path_in_args(self
):
676 expected_patterns_in_output
= self
._setup
_files
()
677 du_output
= self
.get_cephfs_shell_cmd_output('du -r')
679 for expected_output
in expected_patterns_in_output
:
680 if sys_version_info
.major
>= 3:
681 self
.assertRegex(expected_output
, du_output
)
682 elif sys_version_info
.major
< 3:
683 assert re_search(expected_output
, du_output
) != None, "\n" + \
684 "expected_output -\n{}\ndu_output -\n{}\n".format(
685 expected_output
, du_output
)
687 def test_du_with_path_in_args(self
):
688 expected_patterns_in_output
, path_to_files
= self
._setup
_files
(True,
692 for p
in path_to_files
:
694 du_output
= self
.get_cephfs_shell_cmd_output(args
)
696 for expected_output
in expected_patterns_in_output
:
697 if sys_version_info
.major
>= 3:
698 self
.assertRegex(expected_output
, du_output
)
699 elif sys_version_info
.major
< 3:
700 assert re_search(expected_output
, du_output
) != None, "\n" +\
701 "expected_output -\n{}\ndu_output -\n{}\n".format(
702 expected_output
, du_output
)
704 def test_du_with_no_args(self
):
705 expected_patterns_in_output
= self
._setup
_files
()
707 du_output
= self
.get_cephfs_shell_cmd_output('du')
709 for expected_output
in expected_patterns_in_output
:
710 # Since CWD is CephFS root and being non-recursive expect only
712 if expected_output
.find('/') == len(expected_output
) - 1:
713 if sys_version_info
.major
>= 3:
714 self
.assertRegex(expected_output
, du_output
)
715 elif sys_version_info
.major
< 3:
716 assert re_search(expected_output
, du_output
) != None, "\n" + \
717 "expected_output -\n{}\ndu_output -\n{}\n".format(
718 expected_output
, du_output
)
721 class TestDF(TestCephFSShell
):
722 def validate_df(self
, filename
):
723 df_output
= self
.get_cephfs_shell_cmd_output('df '+filename
)
724 log
.info("cephfs-shell df output:\n{}".format(df_output
))
726 shell_df
= df_output
.splitlines()[1].split()
728 block_size
= int(self
.mount_a
.df()["total"]) // 1024
729 log
.info("cephfs df block size output:{}\n".format(block_size
))
731 st_size
= int(self
.mount_a
.stat(filename
)["st_size"])
732 log
.info("cephfs stat used output:{}".format(st_size
))
733 log
.info("cephfs available:{}\n".format(block_size
- st_size
))
735 self
.assertTupleEqual((block_size
, st_size
, block_size
- st_size
),
736 (int(shell_df
[0]), int(shell_df
[1]) , int(shell_df
[2])))
738 def test_df_with_no_args(self
):
740 df_output
= self
.get_cephfs_shell_cmd_output('df')
741 assert df_output
== expected_output
743 def test_df_for_valid_directory(self
):
745 mount_output
= self
.mount_a
.run_shell('mkdir ' + dir_name
)
746 log
.info("cephfs-shell mount output:\n{}".format(mount_output
))
747 self
.validate_df(dir_name
)
749 def test_df_for_invalid_directory(self
):
750 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, 'non-existent-dir')
751 proc
= self
.run_cephfs_shell_cmd('df ' + dir_abspath
)
752 assert proc
.stderr
.getvalue().find('error in stat') != -1
754 def test_df_for_valid_file(self
):
755 s
= 'df test' * 14145016
756 o
= self
.get_cephfs_shell_cmd_output("put - dumpfile", stdin
=s
)
757 log
.info("cephfs-shell output:\n{}".format(o
))
758 self
.validate_df("dumpfile")
761 class TestQuota(TestCephFSShell
):
764 def create_dir(self
):
765 mount_output
= self
.get_cephfs_shell_cmd_output('mkdir ' + self
.dir_name
)
766 log
.info("cephfs-shell mount output:\n{}".format(mount_output
))
768 def set_and_get_quota_vals(self
, input_val
):
769 quota_output
= self
.run_cephfs_shell_cmd('quota set --max_bytes '
770 + input_val
[0] + ' --max_files '
773 log
.info("cephfs-shell quota set output:\n{}".format(quota_output
))
775 quota_output
= self
.get_cephfs_shell_cmd_output('quota get '+ self
.dir_name
)
776 log
.info("cephfs-shell quota get output:\n{}".format(quota_output
))
778 quota_output
= quota_output
.split()
779 return quota_output
[1], quota_output
[3]
783 set_values
= ('6', '2')
784 self
.assertTupleEqual(self
.set_and_get_quota_vals(set_values
), set_values
)
786 def test_replace_values(self
):
788 set_values
= ('20', '4')
789 self
.assertTupleEqual(self
.set_and_get_quota_vals(set_values
), set_values
)
791 def test_set_invalid_dir(self
):
792 set_values
= ('5', '5')
794 self
.assertTupleEqual(self
.set_and_get_quota_vals(set_values
), set_values
)
795 raise Exception("Something went wrong!! Values set for non existing directory")
797 # Test should pass as values cannot be set for non existing directory
800 def test_set_invalid_values(self
):
802 set_values
= ('-6', '-5')
804 self
.assertTupleEqual(self
.set_and_get_quota_vals(set_values
), set_values
)
805 raise Exception("Something went wrong!! Invalid values set")
807 # Test should pass as invalid values cannot be set
810 def test_exceed_file_limit(self
):
812 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, self
.dir_name
)
813 self
.mount_a
.run_shell('touch '+dir_abspath
+'/file1')
814 file2
= path
.join(dir_abspath
, "file2")
816 self
.mount_a
.run_shell('touch '+file2
)
817 raise Exception("Something went wrong!! File creation should have failed")
818 except CommandFailedError
:
819 # Test should pass as file quota set to 2
820 # Additional condition to confirm file creation failure
821 if not path
.exists(file2
):
825 def test_exceed_write_limit(self
):
827 dir_abspath
= path
.join(self
.mount_a
.mountpoint
, self
.dir_name
)
828 filename
= 'test_file'
829 file_abspath
= path
.join(dir_abspath
, filename
)
831 # Write should fail as bytes quota is set to 6
832 sudo_write_file(self
.mount_a
.client_remote
, file_abspath
,
833 'Disk raise Exception')
834 raise Exception("Write should have failed")
835 except CommandFailedError
:
836 # Test should pass only when write command fails
837 path_exists
= path
.exists(file_abspath
)
839 # Testing with teuthology: No file is created.
841 elif path_exists
and not path
.getsize(file_abspath
):
842 # Testing on Fedora 30: When write fails, empty file gets created.
848 class TestXattr(TestCephFSShell
):
851 def create_dir(self
):
852 self
.run_cephfs_shell_cmd('mkdir ' + self
.dir_name
)
854 def set_get_list_xattr_vals(self
, input_val
):
855 setxattr_output
= self
.get_cephfs_shell_cmd_output('setxattr '
859 + ' ' + input_val
[1])
860 log
.info("cephfs-shell setxattr output:\n{}".format(setxattr_output
))
862 getxattr_output
= self
.get_cephfs_shell_cmd_output('getxattr '
864 + ' ' + input_val
[0])
865 log
.info("cephfs-shell getxattr output:\n{}".format(getxattr_output
))
867 listxattr_output
= self
.get_cephfs_shell_cmd_output('listxattr '+ self
.dir_name
)
868 log
.info("cephfs-shell listxattr output:\n{}".format(listxattr_output
))
870 return listxattr_output
, getxattr_output
874 set_values
= ('user.key', '2')
875 self
.assertTupleEqual(self
.set_get_list_xattr_vals(set_values
), set_values
)
877 def test_reset(self
):
879 set_values
= ('user.key', '4')
880 self
.assertTupleEqual(self
.set_get_list_xattr_vals(set_values
), set_values
)
882 def test_non_existing_dir(self
):
883 set_values
= ('user.key', '9')
884 self
.assertTupleEqual(self
.set_get_list_xattr_vals(set_values
), (u
'', u
''))
888 # Test that ls passes
890 # o = self.get_cephfs_shell_cmd_output("ls")
891 # log.info("cephfs-shell output:\n{}".format(o))
893 # o = self.mount_a.run_shell(['ls']).stdout.getvalue().strip().replace("\n", " ").split()
894 # log.info("mount_a output:\n{}".format(o))
896 # # ls should not list hidden files without the -a switch
897 # if '.' in o or '..' in o:
898 # log.info('ls failed')
900 # log.info('ls succeeded')
902 # def test_ls_a(self):
904 # Test that ls -a passes
906 # o = self.get_cephfs_shell_cmd_output("ls -a")
907 # log.info("cephfs-shell output:\n{}".format(o))
909 # o = self.mount_a.run_shell(['ls', '-a']).stdout.getvalue().strip().replace("\n", " ").split()
910 # log.info("mount_a output:\n{}".format(o))
912 # if '.' in o and '..' in o:
913 # log.info('ls -a succeeded')
915 # log.info('ls -a failed')
917 class TestMisc(TestCephFSShell
):
918 def test_issue_cephfs_shell_cmd_at_invocation(self
):
920 Test that `cephfs-shell -c conf cmd` works.
922 # choosing a long name since short ones have a higher probability
923 # of getting matched by coincidence.
924 dirname
= 'somedirectory'
925 self
.run_cephfs_shell_cmd(['mkdir', dirname
])
927 output
= self
.mount_a
.client_remote
.run(args
=['cephfs-shell', '-c',
928 self
.mount_a
.config_path
, 'ls'],
929 stdout
=StringIO()).stdout
.getvalue().strip()
931 if sys_version_info
.major
>= 3:
932 self
.assertRegex(dirname
, output
)
933 elif sys_version_info
.major
< 3:
934 assert re_search(dirname
, output
) != None, "\n" + \
935 "expected_output -\n{}\ndu_output -\n{}\n".format(
940 Test that help outputs commands.
942 o
= self
.get_cephfs_shell_cmd_output("help all")
943 log
.info("output:\n{}".format(o
))
945 class TestConfReading(TestCephFSShell
):
946 def test_reading_conf_opt(self
):
948 Read conf without duplicate sections/options.
950 debugval
= self
.fs
.mon_manager
.raw_cluster_cmd('config', 'get',
951 'client','debug_shell')
952 debugval
= str_to_bool(debugval
)
953 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
954 'debug_shell', str(not debugval
))
955 output
= self
.get_cephfs_shell_cmd_output('set debug')
957 str_to_bool(output
[output
.find('debug: ') + len('debug: ') : ])
958 assert not debugval
== new_debug_val
960 def test_reading_conf_after_setting_opt_twice(self
):
962 Read conf without duplicate sections/options.
964 debugval
= self
.fs
.mon_manager
.raw_cluster_cmd('config', 'get',
965 'client','debug_shell')
966 debugval
= str_to_bool(debugval
)
968 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
969 'debug_shell', str(not debugval
))
970 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
971 'debug_shell', str(not debugval
))
972 output
= self
.get_cephfs_shell_cmd_output('set debug')
974 str_to_bool(output
[output
.find('debug: ') + len('debug: ') : ])
975 assert not debugval
== new_debug_val
977 def test_reading_conf_after_resetting_opt(self
):
978 debugval
= self
.fs
.mon_manager
.raw_cluster_cmd('config', 'get',
979 'client','debug_shell')
980 debugval
= str_to_bool(debugval
)
982 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
983 'debug_shell', str(not debugval
))
984 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'rm', 'client',
986 self
.fs
.mon_manager
.raw_cluster_cmd('config', 'set', 'client',
987 'debug_shell', str(not debugval
))
988 output
= self
.get_cephfs_shell_cmd_output('set debug')
990 str_to_bool(output
[output
.find('debug: ') + len('debug: ') : ])
991 assert not debugval
== new_debug_val