From 9f8883b0f6573463f45b4ebb0d0f791153c0cdf6 Mon Sep 17 00:00:00 2001 From: Jos Collin Date: Tue, 17 Sep 2019 15:01:04 +0530 Subject: [PATCH] mgr/volumes: list FS subvolumes, subvolume groups and their snapshots Fixes: https://tracker.ceph.com/issues/41842 Signed-off-by: Jos Collin (cherry picked from commit 1de980020d546f1f80cb3b60f199d578dc321825) --- src/pybind/mgr/volumes/fs/subvolspec.py | 12 ++++ src/pybind/mgr/volumes/fs/subvolume.py | 21 ++++++ src/pybind/mgr/volumes/fs/volume.py | 88 +++++++++++++++++++++++++ src/pybind/mgr/volumes/module.py | 45 +++++++++++++ 4 files changed, 166 insertions(+) diff --git a/src/pybind/mgr/volumes/fs/subvolspec.py b/src/pybind/mgr/volumes/fs/subvolspec.py index 60fb6d06af009..d00c1e4b1352b 100644 --- a/src/pybind/mgr/volumes/fs/subvolspec.py +++ b/src/pybind/mgr/volumes/fs/subvolspec.py @@ -92,11 +92,23 @@ class SubvolumeSpec(object): """ return os.path.join(self.subvolume_path, snapdir, snapname) + def make_subvol_snapdir_path(self, snapdir): + """ + return the subvolume snapdir path + """ + return os.path.join(self.subvolume_path, snapdir.encode('utf-8')) + def make_group_snap_path(self, snapdir, snapname): """ return the group snapshot path for a given snapshot name """ return os.path.join(self.group_path, snapdir, snapname) + def make_group_snapdir_path(self, snapdir): + """ + return the group's snapdir path + """ + return os.path.join(self.group_path, snapdir.encode('utf-8')) + def __str__(self): return "{0}/{1}".format(self.groupid, self.subvolumeid) diff --git a/src/pybind/mgr/volumes/fs/subvolume.py b/src/pybind/mgr/volumes/fs/subvolume.py index ed3314ac5257c..8f3c2b1ab5ef4 100644 --- a/src/pybind/mgr/volumes/fs/subvolume.py +++ b/src/pybind/mgr/volumes/fs/subvolume.py @@ -189,6 +189,27 @@ class SubVolume(object): raise VolumeException(-e.args[0], e.args[1]) return path + def get_dir_entries(self, path): + """ + Get the directory names in a given path + :param path: the given path + :return: the list of directory names + """ + dirs = [] + try: + with self.fs.opendir(path) as dir_handle: + d = self.fs.readdir(dir_handle) + while d: + if (d.d_name not in (b".", b"..")) and d.is_dir(): + dirs.append(d.d_name) + d = self.fs.readdir(dir_handle) + except cephfs.ObjectNotFound: + # When the given path is not found, we just return an empty list + return [] + except cephfs.Error as e: + raise VolumeException(-e.args[0], e.args[1]) + return dirs + ### group operations def create_group(self, spec, mode=0o755, pool=None): diff --git a/src/pybind/mgr/volumes/fs/volume.py b/src/pybind/mgr/volumes/fs/volume.py index 5a7af604eb71b..fd3874ef22ac8 100644 --- a/src/pybind/mgr/volumes/fs/volume.py +++ b/src/pybind/mgr/volumes/fs/volume.py @@ -359,6 +359,16 @@ class VolumeClient(object): return result return conn_wrapper + def nametojson(self, names): + """ + convert the list of names to json + """ + + namedict = [] + for i in range(len(names)): + namedict.append({'name': names[i].decode('utf-8')}) + return json.dumps(namedict, indent=2) + ### subvolume operations @connection_pool_wrap @@ -425,6 +435,28 @@ class VolumeClient(object): ret = self.volume_exception_to_retval(ve) return ret + @connection_pool_wrap + def list_subvolumes(self, fs_handle, **kwargs): + ret = 0, "", "" + groupname = kwargs['group_name'] + + try: + with SubVolume(self.mgr, fs_handle) as sv: + spec = SubvolumeSpec(None, groupname) + if not self.group_exists(sv, spec): + raise VolumeException( + -errno.ENOENT, "Subvolume group '{0}' not found".format(groupname)) + path = sv.get_group_path(spec) + # When default subvolume group is not yet created we just return an empty list. + if path is None: + ret = 0, '[]', "" + else: + subvolumes = sv.get_dir_entries(path) + ret = 0, self.nametojson(subvolumes), "" + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + ### subvolume snapshot @connection_pool_wrap @@ -477,6 +509,31 @@ class VolumeClient(object): ret = self.volume_exception_to_retval(ve) return ret + @connection_pool_wrap + def list_subvolume_snapshots(self, fs_handle, **kwargs): + ret = 0, "", "" + subvolname = kwargs['sub_name'] + groupname = kwargs['group_name'] + + try: + with SubVolume(self.mgr, fs_handle) as sv: + spec = SubvolumeSpec(subvolname, groupname) + if not self.group_exists(sv, spec): + raise VolumeException( + -errno.ENOENT, "Subvolume group '{0}' not found".format(groupname)) + + if sv.get_subvolume_path(spec) == None: + raise VolumeException(-errno.ENOENT, + "Subvolume '{0}' not found".format(subvolname)) + + path = spec.make_subvol_snapdir_path(self.mgr.rados.conf_get('client_snapdir')) + snapshots = sv.get_dir_entries(path) + ret = 0, self.nametojson(snapshots), "" + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + + ### group operations @connection_pool_wrap @@ -525,6 +582,18 @@ class VolumeClient(object): except VolumeException as ve: return self.volume_exception_to_retval(ve) + @connection_pool_wrap + def list_subvolume_groups(self, fs_handle, **kwargs): + ret = 0, "", "" + + try: + with SubVolume(self.mgr, fs_handle) as sv: + subvolumegroups = sv.get_dir_entries(SubvolumeSpec.DEFAULT_SUBVOL_PREFIX) + ret = 0, self.nametojson(subvolumegroups), "" + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + ### group snapshot @connection_pool_wrap @@ -565,6 +634,25 @@ class VolumeClient(object): ret = self.volume_exception_to_retval(ve) return ret + @connection_pool_wrap + def list_subvolume_group_snapshots(self, fs_handle, **kwargs): + ret = 0, "", "" + groupname = kwargs['group_name'] + + try: + with SubVolume(self.mgr, fs_handle) as sv: + spec = SubvolumeSpec(None, groupname) + if not self.group_exists(sv, spec): + raise VolumeException( + -errno.ENOENT, "Subvolume group '{0}' not found".format(groupname)) + + path = spec.make_group_snapdir_path(self.mgr.rados.conf_get('client_snapdir')) + snapshots = sv.get_dir_entries(path) + ret = 0, self.nametojson(snapshots), "" + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + @connection_pool_wrap def get_subvolume_trash_entry(self, fs_handle, **kwargs): ret = None diff --git a/src/pybind/mgr/volumes/module.py b/src/pybind/mgr/volumes/module.py index 3d7260577f596..e44574c85773d 100644 --- a/src/pybind/mgr/volumes/module.py +++ b/src/pybind/mgr/volumes/module.py @@ -41,6 +41,12 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): 'desc': "Delete a CephFS volume", 'perm': 'rw' }, + { + 'cmd': 'fs subvolumegroup ls ' + 'name=vol_name,type=CephString ', + 'desc': "List subvolumegroups", + 'perm': 'r' + }, { 'cmd': 'fs subvolumegroup create ' 'name=vol_name,type=CephString ' @@ -59,6 +65,13 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): 'desc': "Delete a CephFS subvolume group in a volume", 'perm': 'rw' }, + { + 'cmd': 'fs subvolume ls ' + 'name=vol_name,type=CephString ' + 'name=group_name,type=CephString,req=false ', + 'desc': "List subvolumes", + 'perm': 'r' + }, { 'cmd': 'fs subvolume create ' 'name=vol_name,type=CephString ' @@ -98,6 +111,13 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): "and optionally, in a specific subvolume group", 'perm': 'rw' }, + { + 'cmd': 'fs subvolumegroup snapshot ls ' + 'name=vol_name,type=CephString ' + 'name=group_name,type=CephString ', + 'desc': "List subvolumegroup snapshots", + 'perm': 'r' + }, { 'cmd': 'fs subvolumegroup snapshot create ' 'name=vol_name,type=CephString ' @@ -115,6 +135,14 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): 'desc': "Delete a snapshot of a CephFS subvolume group in a volume", 'perm': 'rw' }, + { + 'cmd': 'fs subvolume snapshot ls ' + 'name=vol_name,type=CephString ' + 'name=sub_name,type=CephString ' + 'name=group_name,type=CephString,req=false ', + 'desc': "List subvolume snapshots", + 'perm': 'r' + }, { 'cmd': 'fs subvolume snapshot create ' 'name=vol_name,type=CephString ' @@ -215,6 +243,10 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): group_name=cmd['group_name'], force=cmd.get('force', False)) + def _cmd_fs_subvolumegroup_ls(self, inbuf, cmd): + vol_name = cmd['vol_name'] + return self.vc.list_subvolume_groups(None, vol_name=cmd['vol_name']) + def _cmd_fs_subvolume_create(self, inbuf, cmd): """ :return: a 3-tuple of return code(int), empty string(str), error message (str) @@ -235,6 +267,10 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): group_name=cmd.get('group_name', None), force=cmd.get('force', False)) + def _cmd_fs_subvolume_ls(self, inbuf, cmd): + return self.vc.list_subvolumes(None, vol_name=cmd['vol_name'], + group_name=cmd.get('group_name', None)) + def _cmd_fs_subvolumegroup_getpath(self, inbuf, cmd): return self.vc.getpath_subvolume_group( None, vol_name=cmd['vol_name'], group_name=cmd['group_name']) @@ -255,6 +291,10 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): snap_name=cmd['snap_name'], force=cmd.get('force', False)) + def _cmd_fs_subvolumegroup_snapshot_ls(self, inbuf, cmd): + return self.vc.list_subvolume_group_snapshots(None, vol_name=cmd['vol_name'], + group_name=cmd['group_name']) + def _cmd_fs_subvolume_snapshot_create(self, inbuf, cmd): return self.vc.create_subvolume_snapshot(None, vol_name=cmd['vol_name'], sub_name=cmd['sub_name'], @@ -267,3 +307,8 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): snap_name=cmd['snap_name'], group_name=cmd.get('group_name', None), force=cmd.get('force', False)) + + def _cmd_fs_subvolume_snapshot_ls(self, inbuf, cmd): + return self.vc.list_subvolume_snapshots(None, vol_name=cmd['vol_name'], + sub_name=cmd['sub_name'], + group_name=cmd.get('group_name', None)) -- 2.39.5