qa/test_backtrace: Validate remote_inode xattr is stored

Validate 'remote_inode' xattr is stored on the
referent inode upon hardlink creation if the
referent inodes feature is enabled. The value
of the xattr would be the inode number of the
primary inode.

Also validate that the referent inode is not
created on hardlink creation if the feature
is disabled.

Fixes: https://tracker.ceph.com/issues/69338
Signed-off-by: Kotresh HR <khiremat@redhat.com>
This commit is contained in:
Kotresh HR 2025-02-28 00:54:43 +05:30
parent 410dcccc79
commit 4e0f7dbd55
2 changed files with 57 additions and 4 deletions

View File

@ -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)

View File

@ -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"])