|
14 | 14 | from tasks.cephfs.cephfs_test_case import CephFSTestCase |
15 | 15 | from tasks.cephfs.fuse_mount import FuseMount |
16 | 16 | from teuthology.contextutil import safe_while |
17 | | -from teuthology.exceptions import CommandFailedError |
| 17 | +from teuthology.exceptions import CommandFailedError, MaxWhileTries |
18 | 18 |
|
19 | 19 | log = logging.getLogger(__name__) |
20 | 20 |
|
@@ -4437,6 +4437,189 @@ def test_create_when_subvol_group_name_is_too_long(self): |
4437 | 4437 | errmsgs='Error ENAMETOOLONG: use shorter group or subvol name, ' |
4438 | 4438 | 'combination of both should be less than 249 characters') |
4439 | 4439 |
|
| 4440 | +class TestPausePurging(TestVolumesHelper): |
| 4441 | + ''' |
| 4442 | + Tests related to config "mgr/volumes/pause_purging". |
| 4443 | + ''' |
| 4444 | + |
| 4445 | + CONF_OPT = 'mgr/volumes/pause_purging' |
| 4446 | + |
| 4447 | + def tearDown(self): |
| 4448 | + # every test will change value of this config option as per its need. |
| 4449 | + # assure that this config option's default value is re-stored during |
| 4450 | + # tearDown() so that there's zero chance that it interferes with next |
| 4451 | + # test. |
| 4452 | + self.config_set('mgr', self.CONF_OPT, False) |
| 4453 | + |
| 4454 | + # ensure purge threads have no jobs left from previous test so that |
| 4455 | + # next test doesn't have to face unnecessary complications. |
| 4456 | + self._wait_for_trash_empty() |
| 4457 | + |
| 4458 | + super().tearDown() |
| 4459 | + |
| 4460 | + def _get_sv_path(self, v, sv): |
| 4461 | + sv_path = self.get_ceph_cmd_stdout(f'fs subvolume getpath {v} {sv}') |
| 4462 | + sv_path = sv_path.strip() |
| 4463 | + # delete slash at the beginning of path |
| 4464 | + sv_path = sv_path[1:] |
| 4465 | + |
| 4466 | + sv_path = os.path.join(self.mount_a.mountpoint, sv_path) |
| 4467 | + return sv_path |
| 4468 | + |
| 4469 | + def _assert_sv_is_absent_in_trash(self, sv, sv_path, sv_files): |
| 4470 | + uuid = self.mount_a.get_shell_stdout('sudo ls volumes/_deleting').\ |
| 4471 | + strip() |
| 4472 | + |
| 4473 | + trash_sv_path = sv_path.replace('_nogroup', f'_deleting/{uuid}') |
| 4474 | + trash_sv_path = trash_sv_path.replace(sv, '') |
| 4475 | + |
| 4476 | + try: |
| 4477 | + sv_files_new = self.mount_a.get_shell_stdout( |
| 4478 | + f'sudo ls {trash_sv_path}') |
| 4479 | + except CommandFailedError as cfe: |
| 4480 | + # in case dir for subvol including files in it are deleted |
| 4481 | + self.assertEqual(cfe.exitstatus, 2) |
| 4482 | + return |
| 4483 | + |
| 4484 | + # in case dir for subvol is undeleted yet (but files inside it are). |
| 4485 | + for filename in sv_files: |
| 4486 | + self.assertNotIn(filename, sv_files_new) |
| 4487 | + |
| 4488 | + def _assert_trashed_sv_is_unpurged(self, sv, sv_path, sv_files): |
| 4489 | + uuid = self.mount_a.get_shell_stdout('sudo ls volumes/_deleting').\ |
| 4490 | + strip() |
| 4491 | + |
| 4492 | + trash_sv_path = sv_path.replace('_nogroup', f'_deleting/{uuid}') |
| 4493 | + trash_sv_path = trash_sv_path.replace(sv, '') |
| 4494 | + sv_files_new = self.mount_a.get_shell_stdout(f'sudo ls {trash_sv_path}').\ |
| 4495 | + strip() |
| 4496 | + |
| 4497 | + for filename in sv_files: |
| 4498 | + self.assertIn(filename, sv_files_new) |
| 4499 | + |
| 4500 | + def test_when_paused_subvol_is_trashed_but_stays_unpurged(self): |
| 4501 | + ''' |
| 4502 | + Test that when MGR config option mgr/volumes/pause_purging is |
| 4503 | + set to true, running "ceph fs subvolume rm" will move the subvolume |
| 4504 | + to trash but not purge it, that is delete the subvolume from trash. |
| 4505 | + ''' |
| 4506 | + v = self.volname |
| 4507 | + sv = 'sv1' |
| 4508 | + |
| 4509 | + self.run_ceph_cmd(f'fs subvolume create {v} {sv} --mode=777') |
| 4510 | + |
| 4511 | + self.config_set('mgr', self.CONF_OPT, True) |
| 4512 | + sv_path = self._get_sv_path(v, sv) |
| 4513 | + self._do_subvolume_io(sv, number_of_files=10) |
| 4514 | + sv_files = self.mount_a.get_shell_stdout(f'sudo ls {sv_path}').\ |
| 4515 | + strip().split('\n') |
| 4516 | + |
| 4517 | + self.run_ceph_cmd(f'fs subvolume rm {v} {sv}') |
| 4518 | + # wait for a bit to ensure that trashed subvolume is not picked by |
| 4519 | + # purged threads eventually. |
| 4520 | + with safe_while(tries=1, sleep=7) as proceed: |
| 4521 | + try: |
| 4522 | + while proceed(): |
| 4523 | + self._assert_trashed_sv_is_unpurged(sv, sv_path, sv_files) |
| 4524 | + except MaxWhileTries: |
| 4525 | + pass |
| 4526 | + |
| 4527 | + def test_on_resuming_unpurged_subvol_is_purged(self): |
| 4528 | + ''' |
| 4529 | + Test that when MGR config option mgr/volumes/pause_purging is |
| 4530 | + is set to false, trashed but unpurged subvolume is purged (fully). |
| 4531 | + ''' |
| 4532 | + v = self.volname |
| 4533 | + sv = 'sv1' |
| 4534 | + |
| 4535 | + self.run_ceph_cmd(f'fs subvolume create {v} {sv} --mode=777') |
| 4536 | + self.config_set('mgr', self.CONF_OPT, True) |
| 4537 | + sv_path = self._get_sv_path(v, sv) |
| 4538 | + self._do_subvolume_io(sv, number_of_files=10) |
| 4539 | + sv_files = self.mount_a.get_shell_stdout(f'sudo ls {sv_path}').\ |
| 4540 | + strip().split('\n') |
| 4541 | + |
| 4542 | + self.run_ceph_cmd(f'fs subvolume rm {v} {sv}') |
| 4543 | + # wait for a bit to ensure that trashed subvolume is not picked by |
| 4544 | + # purged threads eventually. |
| 4545 | + with safe_while(tries=1, sleep=7) as proceed: |
| 4546 | + try: |
| 4547 | + while proceed(): |
| 4548 | + self._assert_trashed_sv_is_unpurged(sv, sv_path, sv_files) |
| 4549 | + except MaxWhileTries: |
| 4550 | + pass |
| 4551 | + |
| 4552 | + # XXX actual test here: test that unpurged subvol is purged |
| 4553 | + self.config_set('mgr', self.CONF_OPT, False) |
| 4554 | + self._wait_for_trash_empty() |
| 4555 | + |
| 4556 | + def _get_trashed_sv_path(self, sv, sv_path): |
| 4557 | + uuid = self.mount_a.get_shell_stdout('sudo ls volumes/_deleting').\ |
| 4558 | + strip() |
| 4559 | + |
| 4560 | + trashed_sv_path = sv_path.replace('_nogroup', f'_deleting/{uuid}') |
| 4561 | + trashed_sv_path = trashed_sv_path.replace(sv, '') |
| 4562 | + return trashed_sv_path |
| 4563 | + |
| 4564 | + def _get_num_of_files_in_trashed_sv(self, trashed_sv_path): |
| 4565 | + trashed_sv_files = self.mount_a.get_shell_stdout( |
| 4566 | + f'sudo ls {trashed_sv_path}').strip().split('\n') |
| 4567 | + return len(trashed_sv_files) |
| 4568 | + |
| 4569 | + def _assert_trashed_sv_has_num_of_files(self, trashed_sv_path, |
| 4570 | + num_of_files): |
| 4571 | + sv_files = self.mount_a.get_shell_stdout( |
| 4572 | + f'sudo ls {trashed_sv_path}').strip().split('\n') |
| 4573 | + |
| 4574 | + self.assertEqual(len(sv_files), num_of_files) |
| 4575 | + |
| 4576 | + def test_pausing_halts_ongoing_purge(self): |
| 4577 | + ''' |
| 4578 | + Test that when MGR config option mgr/volumes/pause_purging is |
| 4579 | + set to true, running "ceph fs subvolume rm" will move the subvolume |
| 4580 | + to trash but (asynchronous) purge of the subvolume won't happen till |
| 4581 | + this config option is not explicitly set to False. |
| 4582 | + ''' |
| 4583 | + v = self.volname |
| 4584 | + sv = 'sv1' |
| 4585 | + |
| 4586 | + self.run_ceph_cmd(f'fs subvolume create {v} {sv} --mode=777') |
| 4587 | + |
| 4588 | + sv_path = self._get_sv_path(v, sv) |
| 4589 | + # adding more files for this test since in once few runs it fails due |
| 4590 | + # to race condition |
| 4591 | + self._do_subvolume_io(sv, number_of_files=200) |
| 4592 | + |
| 4593 | + self.run_ceph_cmd(f'fs subvolume rm {v} {sv}') |
| 4594 | + # XXX actual test here: test that purging halts |
| 4595 | + self.config_set('mgr', self.CONF_OPT, True) |
| 4596 | + trashed_sv_path = self._get_trashed_sv_path(sv, sv_path) |
| 4597 | + NUM_OF_FILES = self._get_num_of_files_in_trashed_sv(trashed_sv_path) |
| 4598 | + time.sleep(2) |
| 4599 | + self._assert_trashed_sv_has_num_of_files(trashed_sv_path, |
| 4600 | + NUM_OF_FILES) |
| 4601 | + |
| 4602 | + def test_on_resuming_partly_purged_subvol_purges_fully(self): |
| 4603 | + ''' |
| 4604 | + Test that when MGR config option mgr/volumes/pause_purging is |
| 4605 | + changed to false, the async purging of a subvolume will resume and |
| 4606 | + also finish, causing trash to be empty. |
| 4607 | + ''' |
| 4608 | + v = self.volname |
| 4609 | + sv = 'sv1' |
| 4610 | + |
| 4611 | + self.run_ceph_cmd(f'fs subvolume create {v} {sv} --mode=777') |
| 4612 | + self._do_subvolume_io(sv, number_of_files=100) |
| 4613 | + |
| 4614 | + self.run_ceph_cmd(f'fs subvolume rm {v} {sv}') |
| 4615 | + self.config_set('mgr', self.CONF_OPT, True) |
| 4616 | + time.sleep(2) |
| 4617 | + |
| 4618 | + # XXX actual test here: test that purging is resumed and finished |
| 4619 | + self.config_set('mgr', self.CONF_OPT, False) |
| 4620 | + self._wait_for_trash_empty() |
| 4621 | + |
| 4622 | + |
4440 | 4623 | class TestSubvolumeGroupSnapshots(TestVolumesHelper): |
4441 | 4624 | """Tests for FS subvolume group snapshot operations.""" |
4442 | 4625 | @unittest.skip("skipping subvolumegroup snapshot tests") |
|
0 commit comments