]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
pybind/mgr/volumes: add getter and setter APIs for snapdir_visibility
authorDhairya Parmar <dparmar@redhat.com>
Fri, 25 Jul 2025 14:45:06 +0000 (20:15 +0530)
committerDhairya Parmar <dparmar@redhat.com>
Thu, 25 Sep 2025 16:41:08 +0000 (22:11 +0530)
Fixes: https://tracker.ceph.com/issues/71740
Signed-off-by: Dhairya Parmar <dparmar@redhat.com>
src/pybind/mgr/volumes/fs/operations/template.py
src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py
src/pybind/mgr/volumes/fs/volume.py
src/pybind/mgr/volumes/module.py

index 7aa953045a1ad97361deee60374b93be74cc3473..6f3ab1b7ad27aac3efe5196afa8acd9e52a15dc0 100644 (file)
@@ -73,6 +73,7 @@ class SubvolumeOpType(Enum):
     EARMARK_GET           = 'earmark-get'
     EARMARK_SET           = 'earmark-set'
     EARMARK_CLEAR          = 'earmark-clear'
+    SNAPSHOT_VISIBILITY   = 'snapshot-visibility'
 
 class SubvolumeTemplate(object):
     VERSION = None # type: int
index 5dc6d14b7348648ed0680cca08d662281f3b25d0..74e57601dfb84cef18a47275b0b9647d88d47586 100644 (file)
@@ -645,3 +645,62 @@ class SubvolumeBase(object):
                       f"subvolume={self.subvol_name} group={self.group_name} "
                       f"reason={me.args[1]}, errno:{-me.args[0]}, {os.strerror(-me.args[0])}")
             raise VolumeException(-me.args[0], me.args[1])
+
+    def snapshot_visibility_set(self, value):
+        if value not in ("true", "false"):
+            raise VolumeException(-errno.EINVAL, "snapshot visibility value invalid")
+
+        subvol_root_path = os.path.dirname(self.path)
+        subvol_v2_path = self.path
+        snaps_visibility_vxattr = "ceph.dir.subvolume.snaps.visible"
+        subvolume_size = 0
+        try:
+            self.fs.setxattr(subvol_root_path, snaps_visibility_vxattr,
+                             str(value).encode('utf-8'), 0)
+        except cephfs.Error as e:
+            raise VolumeException(-e.args[0], e.args[1])
+
+        # in case of a sized subvolume, a new srnode will be assigned to the
+        # volumes/<group-name>/<subvolume-name>/<uuid>/ path when applying
+        # ceph.quota.max_bytes which would assign it with the default value
+        # of is_snapdir_visible flag and right now the child snaprealm
+        # changes are not being compiled and sent to the client by MDS, so until
+        # that gets addressed, as a quick fix apply the vxattr on subvol root
+        # and the uuid path. Once the child snaprealm fix is in place, apply
+        # the vxattr only to subvolume root.
+        try:
+            subvolume_size = self.fs.getxattr(
+                subvol_v2_path, "ceph.quota.max_bytes").decode('utf-8')
+        except cephfs.NoData:
+            # should be non-sized subvol v2 path
+            pass
+        if int(subvolume_size) > 0:
+            try:
+                self.fs.setxattr(subvol_v2_path, snaps_visibility_vxattr,
+                                 str(value).encode('utf-8'), 0)
+            except cephfs.Error as e:
+                raise VolumeException(-e.args[0], e.args[1])
+
+            try:
+                 subvol_v2_path_snapshot_visibility = self.fs.getxattr(subvol_v2_path,
+                                        snaps_visibility_vxattr).decode('utf-8')
+                 if bool(subvol_v2_path_snapshot_visibility) != bool(value):
+                     raise VolumeException(-errno.EINVAL, "could not set "
+                                           f"{snaps_visibility_vxattr} to {value} "
+                                           f"on subvolume v2 path {subvol_v2_path}")
+            except cephfs.Error as e:
+                raise VolumeException(-e.args[0], e.args[1])
+
+        try:
+            return self.fs.getxattr(subvol_root_path,
+                                    snaps_visibility_vxattr).decode('utf-8')
+        except cephfs.Error as e:
+            raise VolumeException(-e.args[0], e.args[1])
+
+    def snapshot_visibility_get(self):
+        subvol_parent_path = os.path.dirname(self.path)
+        try:
+            return self.fs.getxattr(subvol_parent_path,
+                                    "ceph.dir.subvolume.snaps.visible").decode('utf-8')
+        except cephfs.Error as e:
+            raise VolumeException(-e.args[0], e.args[1])
index bd34faeafb5d5eed3a43faf340b814af8bed8072..e556b8f4f4fc77bfb674413a48dcd222173d9626 100644 (file)
@@ -1325,3 +1325,38 @@ class VolumeClient(CephfsClient["Module"]):
         except VolumeException as ve:
             ret = self.volume_exception_to_retval(ve)
         return ret
+
+    def subvolume_snapshot_visibility_set(self, **kwargs):
+        ret = 0, "", ""
+        volname = kwargs['vol_name']
+        subvolname = kwargs['sub_name']
+        groupname = kwargs['group_name']
+        value = kwargs['value']
+
+        try:
+            with open_volume(self, volname) as fs_handle:
+                with open_group(fs_handle, self.volspec, groupname) as group:
+                    with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname,
+                                     SubvolumeOpType.SNAPSHOT_VISIBILITY) as subvolume:
+                        v = subvolume.snapshot_visibility_set(value)
+                        ret = 0, v, ""
+        except VolumeException as ve:
+            ret = self.volume_exception_to_retval(ve)
+        return ret
+
+    def subvolume_snapshot_visibility_get(self, **kwargs):
+        ret = 0, "", ""
+        volname = kwargs['vol_name']
+        subvolname = kwargs['sub_name']
+        groupname = kwargs['group_name']
+
+        try:
+            with open_volume(self, volname) as fs_handle:
+                with open_group(fs_handle, self.volspec, groupname) as group:
+                    with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname,
+                                     SubvolumeOpType.SNAPSHOT_VISIBILITY) as subvolume:
+                        v = subvolume.snapshot_visibility_get()
+                        ret = 0, v, ""
+        except VolumeException as ve:
+            ret = self.volume_exception_to_retval(ve)
+        return ret
index 6f7f1b3fd1558dc31f8d44e6b3fe394077507d67..59711221bf1e90ef45d7021db5805c7623ac1458 100644 (file)
@@ -576,6 +576,23 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
             'desc': "Cancel an pending or ongoing clone operation.",
             'perm': 'r'
         },
+        {
+            'cmd': 'fs subvolume snapshot_visibility set'
+                   ' name=vol_name,type=CephString'
+                   ' name=sub_name,type=CephString'
+                   ' name=value,type=CephString,req=true'
+                   ' name=group_name,type=CephString,req=false',
+            'desc': "Set snapdir visibility for subvolume",
+            'perm': 'rw'
+        },
+        {
+            'cmd': 'fs subvolume snapshot_visibility get'
+                   ' name=vol_name,type=CephString'
+                   ' name=sub_name,type=CephString'
+                   ' name=group_name,type=CephString,req=false',
+            'desc': "Get snapdir visibility for subvolume",
+            'perm': 'rw'
+        },
         # volume ls [recursive]
         # subvolume ls <volume>
         # volume authorize/deauthorize
@@ -1097,3 +1114,16 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
         return self.vc.subvolume_info(vol_name=vol_name,
                                       sub_name=subvol,
                                       group_name=group_name)
+
+    @mgr_cmd_wrap
+    def _cmd_fs_subvolume_snapshot_visibility_set(self, inbuf, cmd):
+        return self.vc.subvolume_snapshot_visibility_set(vol_name=cmd['vol_name'],
+                                                        sub_name=cmd['sub_name'],
+                                                        value=cmd['value'],
+                                                        group_name=cmd.get('group_name', None))
+
+    @mgr_cmd_wrap
+    def _cmd_fs_subvolume_snapshot_visibility_get(self, inbuf, cmd):
+        return self.vc.subvolume_snapshot_visibility_get(vol_name=cmd['vol_name'],
+                                                        sub_name=cmd['sub_name'],
+                                                        group_name=cmd.get('group_name', None))