From: Venky Shankar Date: Thu, 24 Jul 2025 05:07:31 +0000 (+0530) Subject: Merge PR #62682 into main X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=82603e90ea8effa303e3dd68233e19628cfe81e1;p=ceph.git Merge PR #62682 into main * refs/pull/62682/head: Reviewed-by: Patrick Donnelly Reviewed-by: Venky Shankar --- 82603e90ea8effa303e3dd68233e19628cfe81e1 diff --cc qa/tasks/cephfs/test_volumes.py index 6ad69ae6b3d,cda6aa9fd7b..27111d8d178 --- a/qa/tasks/cephfs/test_volumes.py +++ b/qa/tasks/cephfs/test_volumes.py @@@ -6583,225 -6711,132 +6692,350 @@@ class TestSubvolumeSnapshots(TestVolume # Clean tmp config file self.mount_a.run_shell(['sudo', 'rm', '-f', tmp_meta_path], omit_sudo=False) + def test_subvolume_snapshot_with_use_global_snaprealm_seq_config_disabled(self): + """ + To verify that the subvolume snapshots doesn't unnecessarily cow old inodes of + parent directories of subvolume snapshot path when mds_use_global_snaprealm_seq_for_subvol + is disabled + """ + + # Disable the config + self.config_set('mds', 'mds_use_global_snaprealm_seq_for_subvol', False) + self.assertEqual(self.config_get('mds', 'mds_use_global_snaprealm_seq_for_subvol'), 'false') + + subvolname = self._gen_subvol_name() + group = self._gen_subvol_grp_name() + snapshot = self._gen_subvol_snap_name() + self._create_subvolumes_and_snapshots(group, subvolname, snapshot) + + self._verify_old_inodes(group, f"{subvolname}_1", False, False) + + # cleanup + self._cleanup_subvolumes_and_snapshots(group, subvolname, snapshot) + + def test_subvolume_snapshot_with_use_global_snaprealm_seq_config_enabled(self): + """ + To verify that the subvolume snapshots unnecessarily cow old inodes of parent + directories of subvolume snapshot path when mds_use_global_snaprealm_seq_for_subvol + is enabled + """ + + # Enable the config + self.config_set('mds', 'mds_use_global_snaprealm_seq_for_subvol', True) + self.assertEqual(self.config_get('mds', 'mds_use_global_snaprealm_seq_for_subvol'), 'true') + + subvolname = self._gen_subvol_name() + group = self._gen_subvol_grp_name() + snapshot = self._gen_subvol_snap_name() + self._create_subvolumes_and_snapshots(group, subvolname, snapshot) + + self._verify_old_inodes(group, f"{subvolname}_1", True, False) + + # cleanup + self._cleanup_subvolumes_and_snapshots(group, subvolname, snapshot) + + def test_root_snapshot_with_use_global_snaprealm_seq_config_disabled(self): + """ + To verify that the snapshots between root and subvolume snapshot directory triggers cow of + old inodes based on global snaprealm's seq number for snpashots between root and subvolume + directory. Also verify, it uses subvolume snaprealm's seq number for subvolume snapshots. + """ + + # Disable the config + self.config_set('mds', 'mds_use_global_snaprealm_seq_for_subvol', False) + self.assertEqual(self.config_get('mds', 'mds_use_global_snaprealm_seq_for_subvol'), 'false') + + subvolname = self._gen_subvol_name() + group = self._gen_subvol_grp_name() + snapshot = self._gen_subvol_snap_name() + + self._create_subvolumes_and_snapshots(group, subvolname, snapshot, True) + + self._verify_old_inodes(group, f"{subvolname}_1", False, True) + + # cleanup + self._cleanup_subvolumes_and_snapshots(group, subvolname, snapshot, True) + + def test_subvolume_snapshot_with_use_global_snaprealm_seq_config_disabled_with_journal_flush(self): + """ + To verify that the stale old inodes get trimmed during journal flush when the config + mds_use_global_snaprealm_seq_for_subvol is disabled + """ + + # Disable the config + self.config_set('mds', 'mds_use_global_snaprealm_seq_for_subvol', False) + self.assertEqual(self.config_get('mds', 'mds_use_global_snaprealm_seq_for_subvol'), 'false') + + subvolname = self._gen_subvol_name() + group = self._gen_subvol_grp_name() + snapshot = self._gen_subvol_snap_name() + self._create_subvolumes_and_snapshots(group, subvolname, snapshot, False) + + self._verify_old_inodes(group, f"{subvolname}_1", False, False, True) + + # cleanup + self._cleanup_subvolumes_and_snapshots(group, subvolname, snapshot) + + def test_subvolume_snapshot_with_use_global_snaprealm_seq_config_enabled_with_journal_flush(self): + """ + To verify that the stale old inodes get trimmed during journal flush even when the config + mds_use_global_snaprealm_seq_for_subvol is enabled + """ + + # Enable the config + self.config_set('mds', 'mds_use_global_snaprealm_seq_for_subvol', True) + self.assertEqual(self.config_get('mds', 'mds_use_global_snaprealm_seq_for_subvol'), 'true') + + subvolname = self._gen_subvol_name() + group = self._gen_subvol_grp_name() + snapshot = self._gen_subvol_snap_name() + self._create_subvolumes_and_snapshots(group, subvolname, snapshot, False) + + self._verify_old_inodes(group, f"{subvolname}_1", True, False, True) + + # cleanup + self._cleanup_subvolumes_and_snapshots(group, subvolname, snapshot) + + def test_root_snapshot_with_use_global_snaprealm_seq_config_disabled_with_journal_flush(self): + """ + To verify that the stale old inodes get trimmed during journal flush even when the config + mds_use_global_snaprealm_seq_for_subvol is disabled and root snapshot + """ + + # Disable the config + self.config_set('mds', 'mds_use_global_snaprealm_seq_for_subvol', False) + self.assertEqual(self.config_get('mds', 'mds_use_global_snaprealm_seq_for_subvol'), 'false') + + subvolname = self._gen_subvol_name() + group = self._gen_subvol_grp_name() + snapshot = self._gen_subvol_snap_name() + + self._create_subvolumes_and_snapshots(group, subvolname, snapshot, True) + + self._verify_old_inodes(group, f"{subvolname}_1", False, True, True) + + # cleanup + self._cleanup_subvolumes_and_snapshots(group, subvolname, snapshot, True) + +class TestSubvolumeSnapshotGetpath(TestVolumesHelper): + + def get_subvol_uuid(self, subvol_name, group_name=None): + ''' + Return the UUID directory component obtained from the path of + subvolume. + ''' + if group_name: + cmd = (f'fs subvolume getpath {self.volname} {subvol_name} ' + f'{group_name}') + else: + cmd = f'fs subvolume getpath {self.volname} {subvol_name}' + + subvol_path = self.get_ceph_cmd_stdout(cmd).strip() + + subvol_uuid = os.path.basename(subvol_path) + return subvol_uuid + + def construct_snap_path_for_v2(self, subvol_name, snap_name, uuid, + group_name='_nogroup'): + return os.path.join('/volumes', group_name, subvol_name, '.snap', + snap_name, uuid) + + def construct_snap_path_for_v1(self, subvol_name, snap_name, uuid, + group_name='_nogroup'): + return os.path.join('/volumes', group_name, subvol_name, uuid, + '.snap', snap_name) + + def construct_snap_path_for_legacy(self, subvol_name, snap_name, + group_name='_nogroup'): + return os.path.join('/volumes', group_name, subvol_name, '.snap', + snap_name) + + def test_snapshot_getpath(self): + ''' + Test that "ceph fs subvolume snapshot getpath" command returns path to + the specified snapshot in the specified subvolume. + ''' + subvol_name = self._gen_subvol_name() + snap_name = self._gen_subvol_snap_name() + + self.run_ceph_cmd(f'fs subvolume create {self.volname} {subvol_name}') + sv_uuid = self.get_subvol_uuid(subvol_name) + self.run_ceph_cmd(f'fs subvolume snapshot create {self.volname} ' + f'{subvol_name} {snap_name}') + + snap_path = self.get_ceph_cmd_stdout(f'fs subvolume snapshot getpath ' + f'{self.volname} {subvol_name} ' + f'{snap_name}').strip() + exp_snap_path = self.construct_snap_path_for_v2(subvol_name, snap_name, + sv_uuid) + self.assertEqual(snap_path, exp_snap_path) + + def test_snapshot_getpath_in_group(self): + ''' + Test that "ceph fs subvolume snapshot getpath" command returns path to + the specified snapshot in the specified subvolume in the specified + group. + ''' + subvol_name = self._gen_subvol_name() + group_name = self._gen_subvol_grp_name() + snap_name = self._gen_subvol_snap_name() + + self.run_ceph_cmd(f'fs subvolumegroup create {self.volname} {group_name}') + self.run_ceph_cmd(f'fs subvolume create {self.volname} {subvol_name} ' + f'{group_name}') + sv_uuid = self.get_subvol_uuid(subvol_name, group_name) + self.run_ceph_cmd(f'fs subvolume snapshot create {self.volname} ' + f'{subvol_name} {snap_name} {group_name}') + + snap_path = self.get_ceph_cmd_stdout(f'fs subvolume snapshot getpath ' + f'{self.volname} {subvol_name} ' + f'{snap_name} {group_name}')\ + .strip() + exp_snap_path = self.construct_snap_path_for_v2(subvol_name, snap_name, + sv_uuid, group_name) + self.assertEqual(snap_path, exp_snap_path) + + def test_snapshot_getpath_on_retained_subvol(self): + ''' + Test that "ceph fs subvolume snapshot getpath" command returns path to + the specified snapshot in the specified subvolume that was deleted but + snapshots on which is retained. + ''' + subvol_name = self._gen_subvol_name() + snap_name = self._gen_subvol_snap_name() + + self.run_ceph_cmd(f'fs subvolume create {self.volname} {subvol_name}') + sv_uuid = self.get_subvol_uuid(subvol_name) + self.run_ceph_cmd(f'fs subvolume snapshot create {self.volname} ' + f'{subvol_name} {snap_name}') + self.run_ceph_cmd(f'fs subvolume rm {self.volname} {subvol_name} ' + '--retain-snapshots') + + snap_path = self.get_ceph_cmd_stdout(f'fs subvolume snapshot getpath ' + f'{self.volname} {subvol_name} ' + f'{snap_name}').strip() + exp_snap_path = self.construct_snap_path_for_v2(subvol_name, snap_name, + sv_uuid) + self.assertEqual(snap_path, exp_snap_path) + + def test_snapshot_getpath_on_retained_subvol_in_group(self): + ''' + Test that "ceph fs subvolume snapshot getpath" command returns path to + the specified snapshot in the specified subvolume that was deleted but + snapshots on which is retained. And the deleted subvolume is located on + a non-default group. + ''' + subvol_name = self._gen_subvol_name() + group_name = self._gen_subvol_grp_name() + snap_name = self._gen_subvol_snap_name() + + self.run_ceph_cmd(f'fs subvolumegroup create {self.volname} {group_name}') + self.run_ceph_cmd(f'fs subvolume create {self.volname} {subvol_name} ' + f'{group_name}') + sv_uuid = self.get_subvol_uuid(subvol_name, group_name) + self.run_ceph_cmd(f'fs subvolume snapshot create {self.volname} ' + f'{subvol_name} {snap_name} {group_name}') + self.run_ceph_cmd(f'fs subvolume rm {self.volname} {subvol_name} ' + f'{group_name} --retain-snapshots') + + snap_path = self.get_ceph_cmd_stdout(f'fs subvolume snapshot getpath ' + f'{self.volname} {subvol_name} ' + f'{snap_name} {group_name}')\ + .strip() + exp_snap_path = self.construct_snap_path_for_v2(subvol_name, snap_name, + sv_uuid, group_name) + self.assertEqual(snap_path, exp_snap_path) + + def test_snapshot_getpath_for_v1(self): + subvol_name = self._gen_subvol_name() + snap_name = self._gen_subvol_snap_name() + + self._create_v1_subvolume(subvol_name) + sv_uuid = self.get_subvol_uuid(subvol_name) + self.run_ceph_cmd(f'fs subvolume snapshot create {self.volname} ' + f'{subvol_name} {snap_name}') + + snap_path = self.get_ceph_cmd_stdout( + f'fs subvolume snapshot getpath {self.volname} {subvol_name} ' + f'{snap_name}').strip() + exp_snap_path = self.construct_snap_path_for_v1(subvol_name, snap_name, + sv_uuid) + self.assertEqual(snap_path, exp_snap_path) + + def test_snapshot_getpath_in_group_for_v1(self): + subvol_name = self._gen_subvol_name() + group_name = self._gen_subvol_grp_name() + snap_name = self._gen_subvol_snap_name() + + self.run_ceph_cmd(f'fs subvolumegroup create {self.volname} ' + f'{group_name}') + self._create_v1_subvolume(subvol_name, group_name) + sv_uuid = self.get_subvol_uuid(subvol_name, group_name) + self.run_ceph_cmd(f'fs subvolume snapshot create {self.volname} ' + f'{subvol_name} {snap_name} {group_name}') + + snap_path = self.get_ceph_cmd_stdout( + f'fs subvolume snapshot getpath {self.volname} {subvol_name} ' + f'{snap_name} {group_name}').strip() + exp_snap_path = self.construct_snap_path_for_v1(subvol_name, snap_name, + sv_uuid, group_name) + self.assertEqual(snap_path, exp_snap_path) + + def test_snapshot_getpath_for_upgraded_legacy(self): + subvol_name = self._gen_subvol_name() + snap_name = self._gen_subvol_snap_name() + + sv_path = os.path.join('.', 'volumes', '_nogroup', subvol_name) + self.mount_a.run_shell(f'sudo mkdir -p {sv_path}', omit_sudo=False) + + sv_getpath = self.get_ceph_cmd_stdout( + f'fs subvolume getpath {self.volname} {subvol_name}').strip() + self.assertNotEqual(sv_getpath, None) + # remove '/' at the beginning + self.assertEqual(sv_path[1:], sv_getpath) + self._assert_meta_location_and_version(self.volname, subvol_name, + version=1, legacy=True) + + self.run_ceph_cmd(f'fs subvolume snapshot create {self.volname} ' + f'{subvol_name} {snap_name}') + + snap_path = self.get_ceph_cmd_stdout( + f'fs subvolume snapshot getpath {self.volname} {subvol_name} ' + f'{snap_name}').strip() + exp_snap_path = self.construct_snap_path_for_legacy(subvol_name, + snap_name) + self.assertEqual(snap_path, exp_snap_path) + + def test_snapshot_getpath_in_group_for_upgraded_legacy(self): + subvol_name = self._gen_subvol_name() + group_name = self._gen_subvol_grp_name() + snap_name = self._gen_subvol_snap_name() + + sv_path = os.path.join('.', 'volumes', group_name, subvol_name) + self.mount_a.run_shell(f'sudo mkdir -p {sv_path}', omit_sudo=False) + + sv_getpath = self.get_ceph_cmd_stdout( + f'fs subvolume getpath {self.volname} {subvol_name} ' + f'{group_name}').strip() + self.assertNotEqual(sv_getpath, None) + # remove '/' at the beginning + self.assertEqual(sv_path[1:], sv_getpath) + self._assert_meta_location_and_version(self.volname, subvol_name, + subvol_group=group_name, + version=1, legacy=True) + + self.run_ceph_cmd(f'fs subvolume snapshot create {self.volname} ' + f'{subvol_name} {snap_name} {group_name}') + + snap_path = self.get_ceph_cmd_stdout( + f'fs subvolume snapshot getpath {self.volname} {subvol_name} ' + f'{snap_name} {group_name}').strip() + exp_snap_path = self.construct_snap_path_for_legacy(subvol_name, snap_name, + group_name) + self.assertEqual(snap_path, exp_snap_path) + + class TestSubvolumeSnapshotClones(TestVolumesHelper): """ Tests for FS subvolume snapshot clone operations.""" def test_clone_subvolume_info(self):