11import logging
22import time
3+ import os
4+ import signal
35
46log = 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