From 06a869cb8ad1a9e5f966fa49046f6eb6674c29fe Mon Sep 17 00:00:00 2001 From: Kotresh HR Date: Fri, 18 Mar 2022 12:34:32 +0530 Subject: [PATCH] mgr/volumes: Support subvolumegroup quota Fixes: https://tracker.ceph.com/issues/53509 Signed-off-by: Kotresh HR --- src/pybind/mgr/volumes/fs/operations/group.py | 86 +++++++++++++------ src/pybind/mgr/volumes/fs/volume.py | 17 +++- src/pybind/mgr/volumes/module.py | 3 +- 3 files changed, 74 insertions(+), 32 deletions(-) diff --git a/src/pybind/mgr/volumes/fs/operations/group.py b/src/pybind/mgr/volumes/fs/operations/group.py index 152383c51d43a..1409c3e83676d 100644 --- a/src/pybind/mgr/volumes/fs/operations/group.py +++ b/src/pybind/mgr/volumes/fs/operations/group.py @@ -88,14 +88,65 @@ class Group(GroupTemplate): return [] raise +def set_group_attrs(fs, path, attrs): + # set subvolume group attrs + # set size + quota = attrs.get("quota") + if quota is not None: + try: + fs.setxattr(path, 'ceph.quota.max_bytes', str(quota).encode('utf-8'), 0) + except cephfs.InvalidValue: + raise VolumeException(-errno.EINVAL, "invalid size specified: '{0}'".format(quota)) + except cephfs.Error as e: + raise VolumeException(-e.args[0], e.args[1]) -def create_group(fs, vol_spec, groupname, pool, mode, uid, gid): + # set pool layout + pool = attrs.get("data_pool") + if not pool: + pool = get_ancestor_xattr(fs, path, "ceph.dir.layout.pool") + try: + fs.setxattr(path, 'ceph.dir.layout.pool', pool.encode('utf-8'), 0) + except cephfs.InvalidValue: + raise VolumeException(-errno.EINVAL, + "Invalid pool layout '{0}'. It must be a valid data pool".format(pool)) + + # set uid/gid + uid = attrs.get("uid") + if uid is None: + uid = 0 + else: + try: + uid = int(uid) + if uid < 0: + raise ValueError + except ValueError: + raise VolumeException(-errno.EINVAL, "invalid UID") + + gid = attrs.get("gid") + if gid is None: + gid = 0 + else: + try: + gid = int(gid) + if gid < 0: + raise ValueError + except ValueError: + raise VolumeException(-errno.EINVAL, "invalid GID") + fs.chown(path, uid, gid) + + # set mode + mode = attrs.get("mode", None) + if mode is not None: + fs.lchmod(path, mode) + +def create_group(fs, vol_spec, groupname, size, pool, mode, uid, gid): """ create a subvolume group. :param fs: ceph filesystem handle :param vol_spec: volume specification :param groupname: subvolume group name + :param size: In bytes, or None for no size limit :param pool: the RADOS pool where the data objects of the subvolumes will be stored :param mode: the user permissions :param uid: the user identifier @@ -110,32 +161,13 @@ def create_group(fs, vol_spec, groupname, pool, mode, uid, gid): create_base_dir(fs, vol_spec_base_dir, vol_spec.DEFAULT_MODE) fs.mkdir(path, mode) try: - if not pool: - pool = get_ancestor_xattr(fs, path, "ceph.dir.layout.pool") - try: - fs.setxattr(path, 'ceph.dir.layout.pool', pool.encode('utf-8'), 0) - except cephfs.InvalidValue: - raise VolumeException(-errno.EINVAL, - "Invalid pool layout '{0}'. It must be a valid data pool".format(pool)) - if uid is None: - uid = 0 - else: - try: - uid = int(uid) - if uid < 0: - raise ValueError - except ValueError: - raise VolumeException(-errno.EINVAL, "invalid UID") - if gid is None: - gid = 0 - else: - try: - gid = int(gid) - if gid < 0: - raise ValueError - except ValueError: - raise VolumeException(-errno.EINVAL, "invalid GID") - fs.chown(path, uid, gid) + attrs = { + 'uid': uid, + 'gid': gid, + 'data_pool': pool, + 'quota': size + } + set_group_attrs(fs, path, attrs) except (cephfs.Error, VolumeException) as e: try: # cleanup group path on best effort basis diff --git a/src/pybind/mgr/volumes/fs/volume.py b/src/pybind/mgr/volumes/fs/volume.py index 18c5da222fe15..7ba9c6bd9d511 100644 --- a/src/pybind/mgr/volumes/fs/volume.py +++ b/src/pybind/mgr/volumes/fs/volume.py @@ -11,7 +11,8 @@ from .fs_util import listdir from .operations.volume import create_volume, \ delete_volume, rename_volume, list_volumes, open_volume, get_pool_names -from .operations.group import open_group, create_group, remove_group, open_group_unique +from .operations.group import open_group, create_group, remove_group, \ + open_group_unique, set_group_attrs from .operations.subvolume import open_subvol, create_subvol, remove_subvol, \ create_clone from .operations.trash import Trash @@ -738,6 +739,7 @@ class VolumeClient(CephfsClient["Module"]): ret = 0, "", "" volname = kwargs['vol_name'] groupname = kwargs['group_name'] + size = kwargs['size'] pool = kwargs['pool_layout'] uid = kwargs['uid'] gid = kwargs['gid'] @@ -746,13 +748,20 @@ class VolumeClient(CephfsClient["Module"]): try: with open_volume(self, volname) as fs_handle: try: - with open_group(fs_handle, self.volspec, groupname): + with open_group(fs_handle, self.volspec, groupname) as group: # idempotent creation -- valid. - pass + attrs = { + 'uid': uid, + 'gid': gid, + 'mode': octal_str_to_decimal_int(mode), + 'data_pool': pool, + 'quota': size + } + set_group_attrs(fs_handle, group.path, attrs) except VolumeException as ve: if ve.errno == -errno.ENOENT: oct_mode = octal_str_to_decimal_int(mode) - create_group(fs_handle, self.volspec, groupname, pool, oct_mode, uid, gid) + create_group(fs_handle, self.volspec, groupname, size, pool, oct_mode, uid, gid) else: raise except VolumeException as ve: diff --git a/src/pybind/mgr/volumes/module.py b/src/pybind/mgr/volumes/module.py index eb4a82f16565b..aeec5ed3e60eb 100644 --- a/src/pybind/mgr/volumes/module.py +++ b/src/pybind/mgr/volumes/module.py @@ -78,6 +78,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): 'cmd': 'fs subvolumegroup create ' 'name=vol_name,type=CephString ' f'name=group_name,type=CephString,goodchars={goodchars} ' + 'name=size,type=CephInt,req=false ' 'name=pool_layout,type=CephString,req=false ' 'name=uid,type=CephInt,req=false ' 'name=gid,type=CephInt,req=false ' @@ -522,7 +523,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): :return: a 3-tuple of return code(int), empty string(str), error message (str) """ return self.vc.create_subvolume_group( - vol_name=cmd['vol_name'], group_name=cmd['group_name'], + vol_name=cmd['vol_name'], group_name=cmd['group_name'], size=cmd.get('size', None), pool_layout=cmd.get('pool_layout', None), mode=cmd.get('mode', '755'), uid=cmd.get('uid', None), gid=cmd.get('gid', None)) -- 2.39.5