From: Shyamsundar Ranganathan Date: Sun, 23 Aug 2020 16:31:01 +0000 (-0400) Subject: mgr/volumes: Move incarnations for v2 subvolumes, to subvolume trash X-Git-Tag: v15.2.9~13^2~6 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=1db5a116eb44345378ac7d5965dd8f6b56f2ab2c;p=ceph.git mgr/volumes: Move incarnations for v2 subvolumes, to subvolume trash For v2 subvolumes without any snapshots or pending purges move the subvolume to trash, for the purge operation. When removing only incarnations, leverage symlink based purge job creation, to avoid renaming the incarnation out of the subvolume. For create errors, where the subvolume was retained, just remove the created incarnation directory. Fixes: https://tracker.ceph.com/issues/47154 Signed-off-by: Shyamsundar Ranganathan (cherry picked from commit 20c8a62939a0f290234ffa048bcc4c36f033b440) --- diff --git a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py index ce79c4aefdfd..adaeb803cde4 100644 --- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py +++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py @@ -114,6 +114,11 @@ class SubvolumeBase(object): def subvol_type(self): return SubvolumeTypes.from_value(self.metadata_mgr.get_global_option(MetadataManager.GLOBAL_META_KEY_TYPE)) + @property + def purgeable(self): + """ Boolean declaring if subvolume can be purged """ + raise NotImplementedError + def load_config(self): if self.legacy_mode: self.metadata_mgr = MetadataManager(self.fs, self.legacy_config_path, 0o640) diff --git a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py index 49c6020ff616..30003be79604 100644 --- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py +++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py @@ -55,16 +55,17 @@ class SubvolumeV2(SubvolumeV1): self.metadata_mgr.refresh() if self.state == SubvolumeStates.STATE_RETAINED: return True - else: - raise VolumeException(-errno.EINVAL, "invalid state for subvolume '{0}' during create".format(self.subvolname)) + return False except MetadataMgrException as me: if me.errno != -errno.ENOENT: raise VolumeException(me.errno, "internal error while processing subvolume '{0}'".format(self.subvolname)) return False @property - def is_in_use(self): - return not self.path == b'' + def purgeable(self): + if not self.retained or self.list_snapshots() or self.has_pending_purges: + return False + return True @property def has_pending_purges(self): @@ -86,7 +87,6 @@ class SubvolumeV2(SubvolumeV1): except cephfs.Error as e: if e.args[0] == errno.ENOENT: try: - log.debug("creating trash can: {0}".format(self.trash_dir)) self.fs.mkdir(self.trash_dir, 0o700) except cephfs.Error as ce: raise VolumeException(-ce.args[0], ce.args[1]) @@ -131,7 +131,10 @@ class SubvolumeV2(SubvolumeV1): def _remove_on_failure(self, subvol_path, retained): if retained: log.info("cleaning up subvolume incarnation with path: {0}".format(subvol_path)) - self._trash_dir(subvol_path) + try: + self.fs.rmdir(subvol_path) + except cephfs.Error as e: + raise VolumeException(-e.args[0], e.args[1]) else: log.info("cleaning up subvolume with path: {0}".format(self.subvolname)) self.remove() @@ -290,7 +293,6 @@ class SubvolumeV2(SubvolumeV1): self.uid = int(st.st_uid) self.gid = int(st.st_gid) self.mode = int(st.st_mode & ~stat.S_IFMT(st.st_mode)) - self.create_trashcan() except MetadataMgrException as me: if me.errno == -errno.ENOENT: raise VolumeException(-errno.ENOENT, "subvolume '{0}' does not exist".format(self.subvolname)) @@ -303,6 +305,7 @@ class SubvolumeV2(SubvolumeV1): def trash_incarnation_dir(self): """rename subvolume (uuid component) to trash""" + self.create_trashcan() try: bname = os.path.basename(self.path) tpath = os.path.join(self.trash_dir, bname) @@ -316,6 +319,10 @@ class SubvolumeV2(SubvolumeV1): if self.list_snapshots(): if not retainsnaps: raise VolumeException(-errno.ENOTEMPTY, "subvolume '{0}' has snapshots".format(self.subvolname)) + else: + if not self.has_pending_purges: + self.trash_base_dir() + return if self.state != SubvolumeStates.STATE_RETAINED: self.trash_incarnation_dir() self.metadata_mgr.update_global_section(MetadataManager.GLOBAL_META_KEY_PATH, "") @@ -330,9 +337,8 @@ class SubvolumeV2(SubvolumeV1): def remove_snapshot(self, snapname): super(SubvolumeV2, self).remove_snapshot(snapname) - if self.state == SubvolumeStates.STATE_RETAINED and not self.list_snapshots(): - # fake a trash entry for purge threads to find a job - bname = str(uuid.uuid4()).encode('utf-8') - self._link_dir(os.path.join(self.trash_dir, bname), bname) + if self.purgeable: + self.trash_base_dir() # tickle the volume purge job to purge this entry, using ESTALE raise VolumeException(-errno.ESTALE, "subvolume '{0}' has been removed as the last retained snapshot is removed".format(self.subvolname)) + # if not purgeable, subvol is not retained, or has snapshots, or already has purge jobs that will garbage collect this subvol diff --git a/src/pybind/mgr/volumes/fs/purge_queue.py b/src/pybind/mgr/volumes/fs/purge_queue.py index 58e4f067f888..2e2ea41ac058 100644 --- a/src/pybind/mgr/volumes/fs/purge_queue.py +++ b/src/pybind/mgr/volumes/fs/purge_queue.py @@ -42,12 +42,8 @@ def subvolume_purge(volume_client, volname, trashcan, subvolume_trash_entry, sho with open_volume(volume_client, volname) as fs_handle: with open_group(fs_handle, volume_client.volspec, groupname) as group: with open_subvol(fs_handle, volume_client.volspec, group, subvolname, SubvolumeOpType.REMOVE) as subvolume: - log.debug("subvolume.path={0}".format(subvolume.path)) - log.debug("subvolume.is_in_use={0}".format(subvolume.is_in_use)) - log.debug("subvolume.has_pending_purges={0}".format(subvolume.has_pending_purges)) - log.debug("subvolume.list_snapshots={0}".format(subvolume.list_snapshots())) - if subvolume.is_in_use or subvolume.has_pending_purges or subvolume.list_snapshots(): - log.debug("not purging subvolume -- bailing out.") + log.debug("subvolume.path={0}, purgeable={1}".format(subvolume.path, subvolume.purgeable)) + if not subvolume.purgeable: return # this is fine under the global lock -- there are just a handful # of entries in the subvolume to purge. moreover, the purge needs @@ -72,10 +68,9 @@ def purge_trash_entry_for_volume(volume_client, volname, purge_entry, should_can if stat.S_ISLNK(stx['mode']): tgt = fs_handle.readlink(pth, 4096) tgt = tgt[:stx['size']] - log.debug("entry points to subvolume trash: {0}".format(tgt)) + log.debug("purging entry pointing to subvolume trash: {0}".format(tgt)) delink = True try: - log.debug("purging subvolume trash: {0}".format(tgt)) trashcan.purge(tgt, should_cancel) except VolumeException as ve: if not ve.errno == -errno.ENOENT: @@ -87,8 +82,8 @@ def purge_trash_entry_for_volume(volume_client, volname, purge_entry, should_can log.debug("purging trash link: {0}".format(purge_entry)) trashcan.delink(purge_entry) else: - log.debug("entry points to trash: {0}".format(pth)) - trashcan.purge(pth) + log.debug("purging entry pointing to trash: {0}".format(pth)) + trashcan.purge(pth, should_cancel) except cephfs.Error as e: log.warn("failed to remove trash entry: {0}".format(e)) except VolumeException as ve: