diff --git a/qa/tasks/cephfs/filesystem.py b/qa/tasks/cephfs/filesystem.py index 0ff5a28475c..85eac792ca8 100644 --- a/qa/tasks/cephfs/filesystem.py +++ b/qa/tasks/cephfs/filesystem.py @@ -665,6 +665,9 @@ class FilesystemBase(MDSClusterBase): def set_allow_new_snaps(self, yes): self.set_var("allow_new_snaps", yes, '--yes-i-really-mean-it') + def set_allow_referent_inodes(self, yes): + self.set_var("allow_referent_inodes", yes) + def set_bal_rank_mask(self, bal_rank_mask): self.set_var("bal_rank_mask", bal_rank_mask) @@ -1319,9 +1322,13 @@ class FilesystemBase(MDSClusterBase): status = self.status() - def dencoder(self, obj_type, obj_blob): - args = [os.path.join(self._prefix, "ceph-dencoder"), 'type', obj_type, 'import', '-', 'decode', 'dump_json'] + def dencoder(self, obj_type, obj_blob, skip=0): + args = [os.path.join(self._prefix, "ceph-dencoder"), 'type', obj_type] + if skip != 0 : + args.extend(["skip", str(skip)]) + args.extend(['import', '-', 'decode', 'dump_json']) p = self.mon_manager.controller.run(args=args, stdin=BytesIO(obj_blob), stdout=BytesIO()) + return p.stdout.getvalue() def rados(self, *args, **kwargs): @@ -1538,6 +1545,25 @@ class FilesystemBase(MDSClusterBase): args = ["setxattr", obj_name, xattr_name, data] self.rados(args, pool=pool) + def read_remote_inode(self, ino_no, pool=None): + """ + Read the remote_inode xattr from the data pool, return a dict in the + format given by inodeno_t::dump, which is something like: + + :: + + rados -p cephfs_data getxattr 100000001f8.00000000 remote_inode > out.bin + ceph-dencoder type inodeno_t import out.bin decode dump_json + + { + "val": 1099511627778 + } + + :param pool: name of pool to read backtrace from. If omitted, FS must have only + one data pool and that will be used. + """ + return self._read_data_xattr(ino_no, "remote_inode", "inodeno_t", pool) + def read_symlink(self, ino_no, pool=None): return self._read_data_xattr(ino_no, "symlink", "string_wrapper", pool) diff --git a/qa/tasks/cephfs/test_backtrace.py b/qa/tasks/cephfs/test_backtrace.py index cd23c114bfb..658b8ff9f6e 100644 --- a/qa/tasks/cephfs/test_backtrace.py +++ b/qa/tasks/cephfs/test_backtrace.py @@ -5,8 +5,8 @@ from tasks.cephfs.filesystem import ObjectNotFound class TestBacktrace(CephFSTestCase): def test_backtrace(self): """ - That the 'parent' 'layout' and 'symlink' xattrs on the head objects of files - are updated correctly. + That the 'parent', 'layout', 'symlink' and 'remote_inode' xattrs on the + head objects of files are updated correctly. """ old_data_pool_name = self.fs.get_data_pool_name() @@ -36,6 +36,33 @@ class TestBacktrace(CephFSTestCase): "s" : "./file1", }) + # Disabling referent_inodes fs option should not create referent inode and + # and therefore no 'remote_inode' xattr on hardlink creation + self.fs.set_allow_referent_inodes(False); + self.mount_a.run_shell(["mkdir", "hardlink_dir0"]) + self.mount_a.run_shell(["touch", "hardlink_dir0/file1"]) + self.mount_a.run_shell(["ln", "hardlink_dir0/file1", "hardlink_dir0/hl_file1"]) + self.fs.mds_asok(["flush", "journal"]) + + file1_ino = self.mount_a.path_to_ino("hardlink_dir0/file1", follow_symlinks=False) + file1_inode_dump = self.fs.mds_asok(['dump', 'inode', hex(file1_ino)]) + self.assertListEqual(file1_inode_dump['referent_inodes'], []) + + # Enabling referent_inodes fs option should store 'remote_inode' xattr + # on hardlink creation + self.fs.set_allow_referent_inodes(True); + self.mount_a.run_shell(["mkdir", "hardlink_dir"]) + self.mount_a.run_shell(["touch", "hardlink_dir/file1"]) + self.mount_a.run_shell(["ln", "hardlink_dir/file1", "hardlink_dir/hl_file1"]) + self.fs.mds_asok(["flush", "journal"]) + + file1_ino = self.mount_a.path_to_ino("hardlink_dir/file1", follow_symlinks=False) + file1_inode_dump = self.fs.mds_asok(['dump', 'inode', hex(file1_ino)]) + referent_ino = file1_inode_dump["referent_inodes"][0] + + remote_inode_xattr = self.fs.read_remote_inode(referent_ino) + self.assertEqual(remote_inode_xattr['val'], file1_ino) + # Create a file for subsequent checks self.mount_a.run_shell(["mkdir", "parent_a"]) self.mount_a.run_shell(["touch", "parent_a/alpha"])