]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/volumes: Move incarnations for v2 subvolumes, to subvolume trash
authorShyamsundar Ranganathan <srangana@redhat.com>
Sun, 23 Aug 2020 16:31:01 +0000 (12:31 -0400)
committerShyamsundar Ranganathan <srangana@redhat.com>
Thu, 27 Aug 2020 19:42:58 +0000 (15:42 -0400)
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 <srangana@redhat.com>
(cherry picked from commit 20c8a62939a0f290234ffa048bcc4c36f033b440)

src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py
src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py
src/pybind/mgr/volumes/fs/purge_queue.py

index e0e494f9b24f78eaff09868c8d855f57a95a4b7c..ee86bdc85ca171c2e688acd821a07f4cdb21e6f8 100644 (file)
@@ -113,6 +113,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)
index 49c6020ff616d8c382668d10251427b1318c36f9..30003be796043e28816fc7c9817c3a42e11b3f16 100644 (file)
@@ -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
index 58e4f067f888abf9be23ba1fe7636d88db8f658f..2e2ea41ac0587d848167919c2807fe551a9224c5 100644 (file)
@@ -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: