Skip to content

Commit 7b2d9c4

Browse files
committed
qa: add more referent inode tests
This patch adds read, rename, hardlink read after mds restart and kill mds after hardlink operations before flush and restart tests. Fixes: https://tracker.ceph.com/issues/69339 Signed-off-by: Kotresh HR <[email protected]>
1 parent 43b4ef3 commit 7b2d9c4

File tree

1 file changed

+213
-5
lines changed

1 file changed

+213
-5
lines changed

qa/tasks/cephfs/test_referent.py

Lines changed: 213 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import logging
22
import time
3+
import os
4+
import signal
35

46
log = logging.getLogger(__name__)
57

@@ -54,6 +56,23 @@ def assert_purge_idle(self):
5456
self.assertEqual(pq_stats["pq_executing"], 0)
5557
self.assertEqual(pq_stats["pq_executing_ops"], 0)
5658

59+
def verify_referent_inode_list_in_memory(self, p_file, referent_ino):
60+
p_file_ino = self.mount_a.path_to_ino(p_file)
61+
p_file_inode_dump = self.fs.mds_asok(['dump', 'inode', hex(p_file_ino)])
62+
referent_inode_dump = self.fs.mds_asok(['dump', 'inode', hex(referent_ino)])
63+
# referents's remote should point to primary
64+
self.assertEqual(p_file_inode_dump['ino'], referent_inode_dump['remote_ino'])
65+
self.assertIn(referent_ino, p_file_inode_dump['referent_inodes'])
66+
67+
def verify_referent_inode_list_on_disk(self, p_dir, p_file, s_dir, s_file):
68+
p_dir_ino = self.mount_a.path_to_ino(p_dir)
69+
primary_inode = self.fs.read_meta_inode(p_dir_ino, p_file)
70+
s_dir_ino = self.mount_a.path_to_ino(s_dir)
71+
referent_inode = self.fs.read_meta_inode(s_dir_ino, s_file)
72+
# referents's remote should point to primary
73+
self.assertEqual(primary_inode['ino'], referent_inode['remote_ino'])
74+
self.assertIn(referent_inode['ino'], primary_inode['referent_inodes'])
75+
5776
def test_referent_link(self):
5877
"""
5978
test_referent_link - Test creation of referent inode and backtrace on link
@@ -352,10 +371,202 @@ def test_mv_hardlink_cleanup_with_referent(self):
352371
post_reint_bt = self.fs.read_backtrace(file_b_ino)
353372
self.assertEqual(post_reint_bt['ancestors'][0]['dname'], "linkto_b")
354373

355-
def test_multiple_referent_post_reintegration(self):
356-
pass
374+
def test_read_a_referent_dentry(self):
375+
"""
376+
test_read_a_referent_dentry - Test read of a hardlink file with referent inode
377+
"""
378+
self.mount_a.run_shell(["mkdir", "dir0"])
379+
self.mount_a.run_shell(["mkdir", "dir1"])
380+
self.mount_a.write_file('dir0/file1', 'somedata')
381+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file1"])
382+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file2"])
383+
384+
data_read_from_primary_file = self.mount_a.read_file('dir0/file1')
385+
self.assertEqual('somedata', data_read_from_primary_file)
386+
data_read_from_hardlink1 = self.mount_a.read_file('dir1/hardlink_file1')
387+
self.assertEqual('somedata', data_read_from_hardlink1)
388+
data_read_from_hardlink2 = self.mount_a.read_file('dir1/hardlink_file2')
389+
self.assertEqual('somedata', data_read_from_hardlink2)
390+
391+
def test_referent_dentry_load_after_mds_restart(self):
392+
"""
393+
test_referent_dentry_load_after_mds_restart - Test loading of referent inode from disk
394+
"""
395+
self.mount_a.run_shell(["mkdir", "dir0"])
396+
self.mount_a.run_shell(["mkdir", "dir1"])
397+
self.mount_a.write_file('dir0/file1', 'somedata')
398+
399+
# create hardlinks and save referent inode numbers before mds restart
400+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file1"])
401+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file2"])
402+
self.fs.mds_asok(['flush', 'journal'])
403+
file1_ino = self.mount_a.path_to_ino("dir0/file1")
404+
file1_inode_dump = self.fs.mds_asok(['dump', 'inode', hex(file1_ino)])
405+
referent1_ino = file1_inode_dump['referent_inodes'][0]
406+
referent2_ino = file1_inode_dump['referent_inodes'][1]
407+
408+
# umount, flush and restart the mds forcing the mds to load the dentries from the disk
409+
self.mount_a.umount_wait()
410+
self.fs.mds_asok(['flush', 'journal'])
411+
self.fs.mds_fail_restart()
412+
self.fs.wait_for_daemons()
413+
self.mount_a.mount_wait()
414+
415+
# validate the data after loading referent dentry from the disk
416+
data_read_from_primary_file = self.mount_a.read_file('dir0/file1')
417+
self.assertEqual('somedata', data_read_from_primary_file)
418+
data_read_from_hardlink1 = self.mount_a.read_file('dir1/hardlink_file1')
419+
self.assertEqual('somedata', data_read_from_hardlink1)
420+
data_read_from_hardlink2 = self.mount_a.read_file('dir1/hardlink_file2')
421+
self.assertEqual('somedata', data_read_from_hardlink2)
422+
423+
# validate the referent_inodes list after mds restart
424+
file1_inode_dump = self.fs.mds_asok(['dump', 'inode', hex(file1_ino)])
425+
self.assertEqual(len(file1_inode_dump['referent_inodes']), 2)
426+
self.assertIn(referent1_ino, file1_inode_dump['referent_inodes'])
427+
self.assertIn(referent2_ino, file1_inode_dump['referent_inodes'])
357428

358429
def test_rename_a_referent_dentry(self):
430+
"""
431+
test_rename_a_referent_dentry - Test rename of a hardlink file with referent inode
432+
"""
433+
434+
self.mount_a.run_shell(["mkdir", "dir0"])
435+
self.mount_a.run_shell(["mkdir", "dir1"])
436+
self.mount_a.run_shell(["mkdir", "dir2"])
437+
self.mount_a.write_file('dir0/file1', 'somedata')
438+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file1"])
439+
440+
# write out the backtrace - this would writeout the backrace
441+
# of the newly introduced referent inode to the data pool.
442+
self.fs.mds_asok(["flush", "journal"])
443+
# save referent ino for validation from disk
444+
dir1_ino = self.mount_a.path_to_ino('dir1')
445+
referent_inode = self.fs.read_meta_inode(dir1_ino, "hardlink_file1")
446+
referent_ino = referent_inode['ino']
447+
448+
# rename hardlink referent dentry
449+
self.mount_a.run_shell(["mv", "dir1/hardlink_file1", "dir2/renamed_hardlink_file1"])
450+
451+
# validate the data read after rename
452+
data_read_from_renamed_hardlink = self.mount_a.read_file('dir2/renamed_hardlink_file1')
453+
self.assertEqual('somedata', data_read_from_renamed_hardlink)
454+
455+
# validate referent_inode list in memory and on disk
456+
self.fs.mds_asok(["flush", "journal"])
457+
self.verify_referent_inode_list_in_memory("dir0/file1", referent_ino)
458+
self.verify_referent_inode_list_on_disk("dir0", "file1", "dir2", "renamed_hardlink_file1")
459+
# validate backtrace after rename
460+
self.assert_backtrace(referent_ino, "dir2/renamed_hardlink_file1")
461+
462+
def test_rename_primary_to_existing_referent_dentry(self):
463+
"""
464+
test_rename_primary_to_existing_referent_dentry - Test rename primary to it's existing hardlink file.
465+
The rename should be noop as it's rename to same ino
466+
"""
467+
468+
self.mount_a.run_shell(["mkdir", "dir0"])
469+
self.mount_a.run_shell(["mkdir", "dir1"])
470+
self.mount_a.write_file('dir0/file1', 'somedata')
471+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file1"])
472+
primary_inode = self.mount_a.path_to_ino('dir0/file1')
473+
474+
# write out the backtrace - this would writeout the backrace
475+
# of the newly introduced referent inode to the data pool.
476+
self.fs.mds_asok(["flush", "journal"])
477+
# save referent ino for validation from disk
478+
dir1_ino = self.mount_a.path_to_ino('dir1')
479+
referent_inode = self.fs.read_meta_inode(dir1_ino, "hardlink_file1")
480+
referent_ino = referent_inode['ino']
481+
482+
# rename hardlink referent dentry, noop
483+
srcpath = os.path.join(self.mount_a.mountpoint, "dir0", "file1")
484+
dstpath = os.path.join(self.mount_a.mountpoint, "dir1", "hardlink_file1")
485+
os.rename(srcpath, dstpath)
486+
487+
primary_inode_after_rename = self.mount_a.path_to_ino('dir0/file1')
488+
hardlink_inode = self.mount_a.path_to_ino('dir1/hardlink_file1')
489+
self.assertEqual(primary_inode_after_rename, primary_inode)
490+
self.assertEqual(hardlink_inode, primary_inode)
491+
492+
# validate referent_inode list in memory and on disk. Noop after rename
493+
self.verify_referent_inode_list_in_memory("dir0/file1", referent_ino)
494+
self.verify_referent_inode_list_on_disk("dir0", "file1", "dir1", "hardlink_file1")
495+
# validate backtrace after rename. Noop
496+
self.assert_backtrace(referent_ino, "dir1/hardlink_file1")
497+
498+
def test_referent_with_mdlog_replay(self):
499+
"""
500+
test_referent_with_mdlog_replay - Restart the mds before the journal flush so that it recovers referent hardlinks from the journal
501+
"""
502+
self.mount_a.run_shell(["mkdir", "dir0"])
503+
self.mount_a.run_shell(["mkdir", "dir1"])
504+
self.mount_a.run_shell(["mkdir", "dir2"])
505+
self.mount_a.write_file('dir0/file1', 'somedata')
506+
primary_inode = self.mount_a.path_to_ino('dir0/file1')
507+
508+
# hardlinks
509+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file1"])
510+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file2"])
511+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file3"])
512+
self.mount_a.run_shell(["ln", "dir0/file1", "dir1/hardlink_file4"])
513+
514+
# unlink referent hardlink dentry
515+
self.mount_a.run_shell(["rm", "-f", "dir1/hardlink_file4"])
516+
# rename referent hardlink dentry
517+
self.mount_a.run_shell(["mv", "dir1/hardlink_file3", "dir2/renamed_hardlink_file3"])
518+
519+
# restart mds without flush, so that it recovers from the journal replay
520+
self.fs.rank_signal(signal.SIGKILL, rank=0)
521+
self.fs.mds_fail_restart()
522+
self.fs.rank_fail(rank=0)
523+
self.fs.wait_for_daemons()
524+
525+
# Validate after mdlog replay
526+
primary_inode_after_replay = self.mount_a.path_to_ino('dir0/file1')
527+
self.assertEqual(primary_inode, primary_inode_after_replay)
528+
hardlink1_inode = self.mount_a.path_to_ino('dir1/hardlink_file1')
529+
hardlink2_inode = self.mount_a.path_to_ino('dir1/hardlink_file2')
530+
renamed_hardlink3_inode = self.mount_a.path_to_ino('dir2/renamed_hardlink_file3')
531+
hardlink_file3_path = os.path.join(self.mount_a.mountpoint, "dir1", "hardlink_file3")
532+
hardlink_file4_path = os.path.join(self.mount_a.mountpoint, "dir1", "hardlink_file4")
533+
with self.assertRaises(FileNotFoundError):
534+
os.lstat(hardlink_file3_path)
535+
with self.assertRaises(FileNotFoundError):
536+
os.lstat(hardlink_file4_path)
537+
self.assertEqual(primary_inode, hardlink1_inode)
538+
self.assertEqual(primary_inode, hardlink2_inode)
539+
self.assertEqual(primary_inode, renamed_hardlink3_inode)
540+
541+
# referent_inodes list validation
542+
primary_inode_dump = self.fs.mds_asok(['dump', 'inode', hex(primary_inode)])
543+
self.assertEqual(len(primary_inode_dump['referent_inodes']), 3)
544+
self.assertEqual(primary_inode_dump['nlink'], 4)
545+
546+
# flush journal and validate backtraces
547+
self.fs.mds_asok(["flush", "journal"])
548+
dir1_ino = self.mount_a.path_to_ino('dir1')
549+
dir2_ino = self.mount_a.path_to_ino('dir2')
550+
referent_inode1 = self.fs.read_meta_inode(dir1_ino, "hardlink_file1")
551+
referent_inode2 = self.fs.read_meta_inode(dir1_ino, "hardlink_file2")
552+
referent_inode3 = self.fs.read_meta_inode(dir2_ino, "renamed_hardlink_file3")
553+
self.verify_referent_inode_list_in_memory("dir0/file1", referent_inode1['ino'])
554+
self.verify_referent_inode_list_in_memory("dir0/file1", referent_inode2['ino'])
555+
self.verify_referent_inode_list_in_memory("dir0/file1", referent_inode3['ino'])
556+
self.verify_referent_inode_list_on_disk("dir0", "file1", "dir1", "hardlink_file1")
557+
self.verify_referent_inode_list_on_disk("dir0", "file1", "dir1", "hardlink_file2")
558+
self.verify_referent_inode_list_on_disk("dir0", "file1", "dir2", "renamed_hardlink_file3")
559+
with self.assertRaises(ObjectNotFound):
560+
self.fs.read_meta_inode(dir1_ino, "hardlink_file3")
561+
with self.assertRaises(ObjectNotFound):
562+
self.fs.read_meta_inode(dir1_ino, "hardlink_file4")
563+
564+
# validate backtrace after rename
565+
self.assert_backtrace(referent_inode1['ino'], "dir1/hardlink_file1")
566+
self.assert_backtrace(referent_inode2['ino'], "dir1/hardlink_file2")
567+
self.assert_backtrace(referent_inode3['ino'], "dir2/renamed_hardlink_file3")
568+
569+
def test_multiple_referent_post_reintegration(self):
359570
pass
360571

361572
def test_referent_with_mds_killpoints(self):
@@ -364,8 +575,5 @@ def test_referent_with_mds_killpoints(self):
364575
def test_referent_with_snapshot(self):
365576
pass
366577

367-
def test_referent_with_mdlog_replay(self):
368-
pass
369-
370578
def test_referent_no_caps(self):
371579
pass

0 commit comments

Comments
 (0)