diff --git a/src/pybind/mgr/volumes/fs/fs_util.py b/src/pybind/mgr/volumes/fs/fs_util.py index 05a6e52caf5..be0a06acdd8 100644 --- a/src/pybind/mgr/volumes/fs/fs_util.py +++ b/src/pybind/mgr/volumes/fs/fs_util.py @@ -88,6 +88,28 @@ def listdir(fs, dirpath, filter_entries=None): raise VolumeException(-e.args[0], e.args[1]) return dirs + +def has_subdir(fs, dirpath, filter_entries=None): + """ + Check the presence of directory (only dirs) for a given path + """ + res = False + if filter_entries is None: + filter_entries = [b".", b".."] + else: + filter_entries.extend([b".", b".."]) + try: + with fs.opendir(dirpath) as dir_handle: + d = fs.readdir(dir_handle) + while d: + if (d.d_name not in filter_entries) and d.is_dir(): + res = True + break + d = fs.readdir(dir_handle) + except cephfs.Error as e: + raise VolumeException(-e.args[0], e.args[1]) + return res + def is_inherited_snap(snapname): """ Returns True if the snapname is inherited else False diff --git a/src/pybind/mgr/volumes/fs/operations/group.py b/src/pybind/mgr/volumes/fs/operations/group.py index d23f3a44a7f..dac744a306c 100644 --- a/src/pybind/mgr/volumes/fs/operations/group.py +++ b/src/pybind/mgr/volumes/fs/operations/group.py @@ -8,7 +8,7 @@ import cephfs from .snapshot_util import mksnap, rmsnap from .pin_util import pin from .template import GroupTemplate -from ..fs_util import listdir, listsnaps, get_ancestor_xattr, create_base_dir +from ..fs_util import listdir, listsnaps, get_ancestor_xattr, create_base_dir, has_subdir from ..exception import VolumeException log = logging.getLogger(__name__) @@ -64,6 +64,15 @@ class Group(GroupTemplate): return [] raise + def has_subvolumes(self): + try: + return has_subdir(self.fs, self.path) + except VolumeException as ve: + # listing a default group when it's not yet created + if ve.errno == -errno.ENOENT and self.is_default_group(): + return False + raise + def pin(self, pin_type, pin_setting): return pin(self.fs, self.path, pin_type, pin_setting) diff --git a/src/pybind/mgr/volumes/fs/vol_spec.py b/src/pybind/mgr/volumes/fs/vol_spec.py index 422359e03c9..a3ba1308bca 100644 --- a/src/pybind/mgr/volumes/fs/vol_spec.py +++ b/src/pybind/mgr/volumes/fs/vol_spec.py @@ -1,3 +1,9 @@ +from .operations.index import Index +from .operations.group import Group +from .operations.trash import Trash +from .operations.versions.subvolume_base import SubvolumeBase + + class VolSpec(object): """ specification of a "volume" -- base directory and various prefixes. @@ -9,6 +15,8 @@ class VolSpec(object): DEFAULT_NS_PREFIX = "fsvolumens_" # default mode for subvol prefix and group DEFAULT_MODE = 0o755 + # internal directories + INTERNAL_DIRS = [Group.NO_GROUP_NAME, Index.GROUP_NAME, Trash.GROUP_NAME, SubvolumeBase.LEGACY_CONF_DIR] def __init__(self, snapshot_prefix, subvolume_prefix=None, pool_ns_prefix=None): self.snapshot_prefix = snapshot_prefix diff --git a/src/pybind/mgr/volumes/fs/volume.py b/src/pybind/mgr/volumes/fs/volume.py index cd127fc5efb..faa969c52a7 100644 --- a/src/pybind/mgr/volumes/fs/volume.py +++ b/src/pybind/mgr/volumes/fs/volume.py @@ -1,13 +1,14 @@ import json import errno import logging +import os from typing import TYPE_CHECKING import cephfs from mgr_util import CephfsClient -from .fs_util import listdir +from .fs_util import listdir, has_subdir from .operations.volume import create_volume, \ delete_volume, rename_volume, list_volumes, open_volume, get_pool_names @@ -466,6 +467,28 @@ class VolumeClient(CephfsClient["Module"]): ret = self.volume_exception_to_retval(ve) return ret + def subvolume_exists(self, **kwargs): + volname = kwargs['vol_name'] + groupname = kwargs['group_name'] + ret = 0, "", "" + volume_exists = False + + try: + with open_volume(self, volname) as fs_handle: + volume_exists = True + with open_group(fs_handle, self.volspec, groupname) as group: + res = group.has_subvolumes() + if res: + ret = 0, "subvolume exists", "" + else: + ret = 0, "no subvolume exists", "" + except VolumeException as ve: + if volume_exists and ve.errno == -errno.ENOENT: + ret = 0, "no subvolume exists", "" + else: + ret = self.volume_exception_to_retval(ve) + return ret + ### subvolume snapshot def create_subvolume_snapshot(self, **kwargs): @@ -864,6 +887,27 @@ class VolumeClient(CephfsClient["Module"]): ret = self.volume_exception_to_retval(ve) return ret + def subvolume_group_exists(self, **kwargs): + volname = kwargs['vol_name'] + ret = 0, "", "" + volume_exists = False + + try: + with open_volume(self, volname) as fs_handle: + volume_exists = True + res = has_subdir(fs_handle, self.volspec.base_dir, filter_entries=[ + dir.encode('utf-8') for dir in self.volspec.INTERNAL_DIRS]) + if res: + ret = 0, "subvolumegroup exists", "" + else: + ret = 0, "no subvolumegroup exists", "" + except VolumeException as ve: + if volume_exists and ve.errno == -errno.ENOENT: + ret = 0, "no subvolumegroup exists", "" + else: + ret = self.volume_exception_to_retval(ve) + return ret + ### group snapshot def create_subvolume_group_snapshot(self, **kwargs): diff --git a/src/pybind/mgr/volumes/module.py b/src/pybind/mgr/volumes/module.py index 560d85a78bf..7f2e3993cdb 100644 --- a/src/pybind/mgr/volumes/module.py +++ b/src/pybind/mgr/volumes/module.py @@ -111,6 +111,12 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): 'desc': "Resize a CephFS subvolume group", 'perm': 'rw' }, + { + 'cmd': 'fs subvolumegroup exist ' + 'name=vol_name,type=CephString ', + 'desc': "Check a volume for the existence of subvolumegroup", + 'perm': 'r' + }, { 'cmd': 'fs subvolume ls ' 'name=vol_name,type=CephString ' @@ -210,6 +216,14 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): "and optionally, in a specific subvolume group", 'perm': 'r' }, + { + 'cmd': 'fs subvolume exist ' + 'name=vol_name,type=CephString ' + 'name=group_name,type=CephString,req=false ', + 'desc': "Check a volume for the existence of a subvolume, " + "optionally in a specified subvolume group", + 'perm': 'r' + }, { 'cmd': 'fs subvolume metadata set ' 'name=vol_name,type=CephString ' @@ -567,6 +581,10 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): def _cmd_fs_subvolumegroup_ls(self, inbuf, cmd): return self.vc.list_subvolume_groups(vol_name=cmd['vol_name']) + @mgr_cmd_wrap + def _cmd_fs_subvolumegroup_exist(self, inbuf, cmd): + return self.vc.subvolume_group_exists(vol_name=cmd['vol_name']) + @mgr_cmd_wrap def _cmd_fs_subvolume_create(self, inbuf, cmd): """ @@ -657,6 +675,11 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): sub_name=cmd['sub_name'], group_name=cmd.get('group_name', None)) + @mgr_cmd_wrap + def _cmd_fs_subvolume_exist(self, inbuf, cmd): + return self.vc.subvolume_exists(vol_name=cmd['vol_name'], + group_name=cmd.get('group_name', None)) + @mgr_cmd_wrap def _cmd_fs_subvolume_metadata_set(self, inbuf, cmd): return self.vc.set_user_metadata(vol_name=cmd['vol_name'],